Ability to getOrCreateStack by windowingMode/activityType in AM.
Another step away from using static stack ids for things.
Test: Existing tests pass.
Test: go/wm-smoke
Bug: 64146578
Change-Id: Iac05046c96d10e5b26d444172341f2ecf9efe3ee
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5e2e333..b38caf8a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -675,7 +675,7 @@
/** First static stack ID.
* @hide */
- public static final int FIRST_STATIC_STACK_ID = 0;
+ private static final int FIRST_STATIC_STACK_ID = 0;
/** Home activity stack ID. */
public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
@@ -700,7 +700,7 @@
/** Last static stack stack ID.
* @hide */
- public static final int LAST_STATIC_STACK_ID = ASSISTANT_STACK_ID;
+ private static final int LAST_STATIC_STACK_ID = ASSISTANT_STACK_ID;
/** Start of ID range used by stacks that are created dynamically.
* @hide */
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 0cb3804..6b40538 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -500,10 +500,15 @@
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
- if (mActivityType == ACTIVITY_TYPE_ASSISTANT) {
+ return supportSplitScreenWindowingMode(mWindowingMode, mActivityType);
+ }
+
+ /** @hide */
+ public static boolean supportSplitScreenWindowingMode(int windowingMode, int activityType) {
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
return false;
}
- return mWindowingMode != WINDOWING_MODE_FREEFORM && mWindowingMode != WINDOWING_MODE_PINNED;
+ return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
}
private static String windowingModeToString(@WindowingMode int windowingMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 4d33216..ee05d81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,11 +16,6 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -32,7 +27,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 5da9fb2..5049a5c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,6 +16,20 @@
package com.android.server.am;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.getStackIdForActivityType;
+import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
@@ -27,10 +41,12 @@
import static com.android.server.am.proto.ActivityDisplayProto.ID;
import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
import android.util.IntArray;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.ConfigurationContainer;
import java.util.ArrayList;
@@ -129,6 +145,160 @@
return null;
}
+ /**
+ * @return the topmost stack on the display that is compatible with the input windowing mode and
+ * activity type. {@code null} means no compatible stack on the display.
+ * @see ConfigurationContainer#isCompatible(int, int)
+ */
+ <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ // TODO: Should undefined windowing and activity type be compatible with standard type?
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return (T) stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see #getStack(int, int)
+ * @see #createStack(int, int, boolean)
+ */
+ <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+ boolean onTop) {
+ T stack = getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ return createStack(windowingMode, activityType, onTop);
+ }
+
+ /** Creates a stack matching the input windowing mode and activity type on this display. */
+ <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+
+ if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
+ // For now there can be only one stack of a particular non-standard activity type on a
+ // display. So, get that ignoring whatever windowing mode it is currently in.
+ T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ + activityType + " already on display=" + this + ". Can't have multiple.");
+ }
+ }
+
+ final ActivityManagerService service = mSupervisor.mService;
+ if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+ service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
+ service.mSupportsPictureInPicture, activityType)) {
+ throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ + windowingMode);
+ }
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // TODO: Should be okay to have stacks with with undefined windowing mode long term, but
+ // have to set them to something for now due to logic that depending on them.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ final boolean inSplitScreenMode = hasSplitScreenStack();
+ if (!inSplitScreenMode
+ && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType)) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ int stackId = INVALID_STACK_ID;
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ // TODO: Will be removed once we are no longer using static stack ids.
+ stackId = getStackIdForActivityType(activityType);
+ if (stackId == INVALID_STACK_ID) {
+ stackId = getStackIdForWindowingMode(windowingMode);
+ }
+ if (stackId == INVALID_STACK_ID) {
+ // Whatever...put in fullscreen stack for now.
+ stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ }
+ final T stack = getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+
+ if (stackId == INVALID_STACK_ID) {
+ stackId = mSupervisor.getNextStackId();
+ }
+
+ final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
+
+ if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally need to be on
+ // the other side of the docked stack and we make visibility decisions based on that.
+ // TODO: Not sure if this is needed after we change to calculate visibility based on
+ // stack z-order vs. id.
+ getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop);
+ }
+
+ return stack;
+ }
+
+ @VisibleForTesting
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ switch (windowingMode) {
+ case WINDOWING_MODE_PINNED:
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ default:
+ return (T) new ActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ }
+ }
+
+ /** Removes all stacks in the input windowing mode from the system */
+ void removeStacksInWindowingMode(int windowingMode) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getWindowingMode() == windowingMode) {
+ mSupervisor.removeStackLocked(stack.mStackId);
+ }
+ }
+ }
+
+ /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */
+ int getTopVisibleStackActivityType(int excludeWindowingMode) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getWindowingMode() == excludeWindowingMode) {
+ continue;
+ }
+ if (stack.shouldBeVisible(null /* starting */)) {
+ return stack.getActivityType();
+ }
+ }
+ return ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ ActivityStack getSplitScreenStack() {
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasSplitScreenStack() {
+ return getSplitScreenStack() != null;
+ }
+
+ PinnedActivityStack getPinnedStack() {
+ return getStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasPinnedStack() {
+ return getPinnedStack() != null;
+ }
+
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7295bcb9..2c39d8b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,14 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.getActivityTypeForStackId;
+import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.ActivityManager.StackId.isStaticStack;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -164,7 +172,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
@@ -177,7 +184,6 @@
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.proto.ActivityManagerServiceProto.ACTIVITIES;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
@@ -2372,11 +2378,7 @@
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingMode(WINDOWING_MODE_PINNED);
}
}
} break;
@@ -8105,7 +8107,7 @@
final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
- final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+ final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -9862,7 +9864,7 @@
if (tr.mBounds != null) {
rti.bounds = new Rect(tr.mBounds);
}
- rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen();
+ rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
rti.resizeMode = tr.mResizeMode;
ActivityRecord base = null;
@@ -10181,21 +10183,23 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- int stackId = task.getStackId();
+ ActivityStack stack = task.getStack();
if (!task.getWindowConfiguration().canResizeTask()) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
- if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
- stackId = FREEFORM_WORKSPACE_STACK_ID;
+ if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+ } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
}
// Reparent the task to the right stack if necessary
boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
- if (stackId != task.getStackId()) {
+ if (stack != task.getStack()) {
// Defer resume until the task is resized below
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
DEFER_RESUME, "resizeTask");
preserveWindow = false;
}
@@ -10525,13 +10529,15 @@
public int createStackOnDisplay(int displayId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (this) {
- final int stackId = mStackSupervisor.getNextStackId();
- final ActivityStack stack =
- mStackSupervisor.createStackOnDisplay(stackId, displayId, true /*onTop*/);
- if (stack == null) {
+ final ActivityDisplay display =
+ mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
+ if (display == null) {
return INVALID_STACK_ID;
}
- return stack.mStackId;
+ // TODO(multi-display): Have the caller pass in the windowing mode and activity type.
+ final ActivityStack stack = display.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/);
+ return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
@@ -10563,8 +10569,12 @@
"exitFreeformMode: You can only go fullscreen from freeform.");
}
+ final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
- r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ r.getTask().reparent(fullscreenStack, ON_TOP,
REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode");
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10594,8 +10604,20 @@
mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
null /* initialBounds */);
}
- task.reparent(stackId, toTop,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack");
+
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ if (!isStaticStack(stackId)) {
+ throw new IllegalStateException(
+ "moveTaskToStack: No stack for stackId=" + stackId);
+ }
+ stack = mStackSupervisor.getDefaultDisplay().createStack(
+ getWindowingModeForStackId(stackId,
+ mStackSupervisor.getDefaultDisplay().hasSplitScreenStack()),
+ getActivityTypeForStackId(stackId), toTop);
+ }
+ task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+ "moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10628,13 +10650,18 @@
Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
return false;
}
-
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final ActivityStack stack = display.getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(),
+ toTop);
+
// Defer resuming until we move the home stack to the front below
- final boolean moved = task.reparent(DOCKED_STACK_ID, toTop,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ final boolean moved = task.reparent(stack, toTop,
REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
"moveTaskToDockedStack");
if (moved) {
@@ -10682,17 +10709,16 @@
try {
synchronized (this) {
if (animate) {
- if (stackId == PINNED_STACK_ID) {
- final PinnedActivityStack pinnedStack =
- mStackSupervisor.getStack(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */,
- destBounds, animationDuration, false /* fromFullscreen */);
- }
- } else {
+ final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+ if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalArgumentException("Stack: " + stackId
+ " doesn't support animated resize.");
}
+ stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+ animationDuration, false /* fromFullscreen */);
} else {
mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
@@ -10760,8 +10786,12 @@
+ taskId);
}
- final ActivityStack stack = mStackSupervisor.getStack(stackId, CREATE_IF_NEEDED,
- !ON_TOP);
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ if (stack == null) {
+ throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+ + stackId);
+ }
// TODO: Have the callers of this API call a separate reparent method if that is
// what they intended to do vs. having this method also do reparenting.
@@ -10770,8 +10800,8 @@
stack.positionChildAt(task, position);
} else {
// Reparent to new stack.
- task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE,
- !ANIMATE, !DEFER_RESUME, "positionTaskInStack");
+ task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+ !DEFER_RESUME, "positionTaskInStack");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10849,11 +10879,7 @@
}
// When a task is locked, dismiss the pinned stack if it exists
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingMode(WINDOWING_MODE_PINNED);
// isAppPinning is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 0c8321d..fdcb8c6 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,12 +2,9 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -32,13 +29,10 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.SystemClock;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 1934436..7b0b942 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
@@ -103,7 +102,6 @@
import static com.android.server.am.ActivityStack.LAUNCH_TICK;
import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
@@ -1069,6 +1067,11 @@
return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
}
+ ActivityDisplay getDisplay() {
+ final ActivityStack stack = getStack();
+ return stack != null ? stack.getDisplay() : null;
+ }
+
boolean changeWindowTranslucency(boolean toOpaque) {
if (fullscreen == toOpaque) {
return false;
@@ -1134,10 +1137,12 @@
* @return whether this activity supports split-screen multi-window and can be put in the docked
* stack.
*/
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
- return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ return super.supportsSplitScreenWindowingMode()
+ && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1198,7 +1203,8 @@
boolean isKeyguardLocked = service.isKeyguardLocked();
boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
- boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+ final ActivityDisplay display = getDisplay();
+ boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
// is in an incorrect state
boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
@@ -1544,8 +1550,9 @@
if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) {
// On devices that support leanback only (Android TV), Recents activity can only be
// visible if the home stack is the focused stack or we are in split-screen mode.
- isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
- || mStackSupervisor.isFocusedStack(getStack());
+ final ActivityDisplay display = getDisplay();
+ boolean hasSplitScreenStack = display != null && display.hasSplitScreenStack();
+ isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack());
}
return isVisible;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 82966932..76b4ae2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -23,12 +23,11 @@
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.getActivityTypeForStackId;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -77,7 +76,6 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -112,7 +110,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -243,11 +240,6 @@
DESTROYED
}
- // Stack is not considered visible.
- static final int STACK_INVISIBLE = 0;
- // Stack is considered visible
- static final int STACK_VISIBLE = 1;
-
@VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
// Task is being completely removed from all stacks in the system.
@@ -461,7 +453,7 @@
}
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
- boolean onTop) {
+ int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
@@ -471,7 +463,8 @@
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
mTmpRect2.setEmpty();
- updateOverrideConfiguration();
+ setWindowingMode(windowingMode);
+ setActivityType(activityType);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -485,20 +478,6 @@
return mWindowContainerController;
}
- // TODO: Not needed once we are no longer using stack ids as the override config. can be passed
- // in.
- private void updateOverrideConfiguration() {
- final int windowingMode = getWindowingModeForStackId(
- mStackId, mStackSupervisor.getStack(DOCKED_STACK_ID) != null);
- if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- setWindowingMode(windowingMode);
- }
- final int activityType = getActivityTypeForStackId(mStackId);
- if (activityType != ACTIVITY_TYPE_UNDEFINED) {
- setActivityType(activityType);
- }
- }
-
/** Adds the stack to specified display and calls WindowManager to do the same. */
void reparent(ActivityDisplay activityDisplay, boolean onTop) {
removeFromDisplay();
@@ -518,8 +497,7 @@
* @param activityDisplay New display to which this stack was attached.
* @param bounds Updated bounds.
*/
- private void postAddToDisplay(ActivityDisplay activityDisplay,
- Rect bounds, boolean onTop) {
+ private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
mDisplayId = activityDisplay.mDisplayId;
mBounds = bounds != null ? new Rect(bounds) : null;
mFullscreen = mBounds == null;
@@ -571,12 +549,8 @@
return mStackSupervisor.getActivityDisplay(mDisplayId);
}
- void getDisplaySize(Point out) {
- getDisplay().mDisplay.getSize(out);
- }
-
/**
- * @see ActivityStack.getStackDockedModeBounds(Rect, Rect, Rect, boolean)
+ * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean)
*/
void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
Rect outTempTaskBounds, boolean ignoreVisibility) {
@@ -1068,7 +1042,6 @@
"Launch completed; removing icicle of " + r.icicle);
}
-
private void startLaunchTraces(String packageName) {
if (mFullyDrawnStartTime != 0) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
@@ -1483,7 +1456,7 @@
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
+ || getDisplay().hasPinnedStack()) {
mService.mTaskChangeNotificationController.notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
@@ -1591,7 +1564,8 @@
return true;
}
- final ArrayList<ActivityStack> displayStacks = getDisplay().mStacks;
+ final ActivityDisplay display = getDisplay();
+ final ArrayList<ActivityStack> displayStacks = display.mStacks;
final int stackIndex = displayStacks.indexOf(this);
if (stackIndex == displayStacks.size() - 1) {
@@ -1603,8 +1577,10 @@
// Check position and visibility of this stack relative to the front stack on its display.
final ActivityStack topStack = getTopStackOnDisplay();
final int topStackId = topStack.mStackId;
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
- if (mStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isActivityTypeAssistant()) {
@@ -1616,9 +1592,10 @@
// Set home stack to invisible when it is below but not immediately below the docked stack
// A case would be if recents stack exists but has no tasks and is below the docked stack
// and home stack is below recents
- if (mStackId == HOME_STACK_ID) {
- int dockedStackIndex = displayStacks.indexOf
- (mStackSupervisor.getStack(DOCKED_STACK_ID));
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ final ActivityStack splitScreenStack = display.getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ int dockedStackIndex = displayStacks.indexOf(splitScreenStack);
if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
return false;
}
@@ -1630,19 +1607,28 @@
displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
- final int stackBehindTopId = (stackBehindTopIndex >= 0)
- ? displayStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+ final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
+ ? displayStacks.get(stackBehindTopIndex) : null;
+ int stackBehindTopId = INVALID_STACK_ID;
+ int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
+ int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
+ if (stackBehindTop != null) {
+ stackBehindTopId = stackBehindTop.mStackId;
+ stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
+ stackBehindTopActivityType = stackBehindTop.getActivityType();
+ }
+
final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
- if (topStackId == DOCKED_STACK_ID || alwaysOnTop) {
+ if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
if (stackIndex == stackBehindTopIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
return true;
} else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
// Otherwise, this stack can also be visible if it is directly behind a docked stack
// or translucent assistant stack behind an always-on-top top-most stack
- if (stackBehindTopId == DOCKED_STACK_ID) {
+ if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
return true;
- } else if (stackBehindTopId == ASSISTANT_STACK_ID) {
+ } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
starting, mStackId);
}
@@ -1658,8 +1644,8 @@
return true;
}
if (stackBehindTopIndex >= 0) {
- if ((stackBehindTopId == DOCKED_STACK_ID
- || stackBehindTopId == PINNED_STACK_ID)
+ if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED)
&& stackIndex == (stackBehindTopIndex - 1)) {
// The stack behind the docked or pinned stack is also visible so we can have a
// complete backdrop to the translucent activity when the docked stack is up.
@@ -1727,8 +1713,8 @@
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
- final boolean stackVisible = shouldBeVisible(starting);
- boolean behindFullscreenActivity = !stackVisible;
+ final boolean stackShouldBeVisible = shouldBeVisible(starting);
+ boolean behindFullscreenActivity = !stackShouldBeVisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -1754,7 +1740,7 @@
final boolean reallyVisible = checkKeyguardVisibility(r,
visibleIgnoringKeyguard, isTop);
if (visibleIgnoringKeyguard) {
- behindFullscreenActivity = updateBehindFullscreen(!stackVisible,
+ behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, task, r);
}
if (reallyVisible) {
@@ -1792,7 +1778,7 @@
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.state
- + " stackVisible=" + stackVisible
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ " behindFullscreenActivity=" + behindFullscreenActivity
+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
makeInvisible(r);
@@ -1802,10 +1788,10 @@
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
- behindFullscreenActivity = !stackVisible;
+ behindFullscreenActivity = !stackShouldBeVisible;
} else if (mStackId == HOME_STACK_ID) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
- + " stackVisible=" + stackVisible
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// No other task in the home stack should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
@@ -4891,7 +4877,7 @@
}
ci.numActivities = numActivities;
ci.numRunning = numRunning;
- ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen();
+ ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
ci.resizeMode = task.mResizeMode;
list.add(ci);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f423ce8..424caa0 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,16 +24,12 @@
import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.ActivityManager.StackId.getStackIdForActivityType;
-import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -87,8 +83,6 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import static com.android.server.am.ActivityStack.STACK_VISIBLE;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -100,8 +94,6 @@
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
-import static com.android.server.am.proto.ActivityDisplayProto.ID;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
@@ -121,6 +113,7 @@
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -172,7 +165,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.proto.ActivityDisplayProto;
import com.android.server.wm.ConfigurationContainer;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
@@ -619,8 +611,8 @@
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
- mHomeStack = mFocusedStack = mLastFocusedStack =
- getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
@@ -676,7 +668,8 @@
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = getStack(RECENTS_STACK_ID);
+ final ActivityStack recentsStack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
}
@@ -1163,8 +1156,7 @@
void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) {
// Gather all of the running tasks for each stack into runningTaskLists.
- ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists =
- new ArrayList<ArrayList<RunningTaskInfo>>();
+ ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>();
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -2107,19 +2099,19 @@
final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
task.updateOverrideConfiguration(bounds);
- int stackId = getLaunchStackId(null, options, task);
+ ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
- if (stackId != currentStack.mStackId) {
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
- DEFER_RESUME, "findTaskToMoveToFrontLocked");
- stackId = currentStack.mStackId;
+ if (stack != currentStack) {
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
+ "findTaskToMoveToFrontLocked");
+ stack = currentStack;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- resizeStackLocked(stackId, bounds,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
+ if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
+ resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
@@ -2160,25 +2152,46 @@
return null;
}
- protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
- boolean createOnTop) {
- final ActivityStack stack = getStack(stackId);
- if (stack != null) {
- return (T) stack;
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
}
- if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
- return null;
+ if (!supportsMultiWindow) {
+ return false;
}
- if (stackId == DOCKED_STACK_ID) {
- // Make sure recents stack exist when creating a dock stack as it normally need to be on
- // the other side of the docked stack and we make visibility decisions based on that.
- getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, createOnTop);
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType);
}
- return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
}
private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task) {
+ @Nullable TaskRecord task, int activityType) {
// First preference if the windowing mode in the activity options if set.
int windowingMode = (options != null)
@@ -2196,47 +2209,47 @@
}
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- if (!mService.mSupportsMultiWindow && windowingMode != WINDOWING_MODE_FULLSCREEN) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mService.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
}
- if (!mService.mSupportsSplitScreenMultiWindow
- && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ if (isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType)) {
+ return windowingMode;
}
-
- if (windowingMode == WINDOWING_MODE_FREEFORM
- && !mService.mSupportsFreeformWindowManagement) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- }
-
- return windowingMode;
+ return WINDOWING_MODE_FULLSCREEN;
}
- private int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable TaskRecord task) {
- // First preference if the activity type in the activity options if set.
- int activityType = (options != null)
- ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED;
-
+ // Preference is given to the activity type for the activity then the task since the type
+ // once set shouldn't change.
+ int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+ activityType = task.getActivityType();
+ }
if (activityType != ACTIVITY_TYPE_UNDEFINED) {
return activityType;
}
-
- // If activity type is unset, then next preference is the task, then the activity record.
- if (task != null) {
- activityType = task.getActivityType();
- }
- if (activityType == ACTIVITY_TYPE_UNDEFINED && r != null) {
- activityType = r.getActivityType();
- }
- return activityType;
+ return options != null ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED;
}
- int getLaunchStackId(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord candidateTask) {
- return getLaunchStackId(r, options, candidateTask, INVALID_DISPLAY);
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+ return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY);
}
/**
@@ -2248,8 +2261,9 @@
*
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
- int getLaunchStackId(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord candidateTask, int candidateDisplayId) {
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+ int candidateDisplayId) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
//Rect bounds = null;
@@ -2271,13 +2285,13 @@
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options);
options.setLaunchTaskId(taskId);
if (task != null) {
- return task.getStack().mStackId;
+ return task.getStack();
}
}
- final int windowingMode = resolveWindowingMode(r, options, candidateTask);
final int activityType = resolveActivityType(r, options, candidateTask);
- ActivityStack stack = null;
+ int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ T stack = null;
// Next preference for stack goes to the display Id set in the activity options or the
// candidate display.
@@ -2287,18 +2301,17 @@
if (displayId != INVALID_DISPLAY) {
if (r != null) {
// TODO: This should also take in the windowing mode and activity type into account.
- stack = getValidLaunchStackOnDisplay(displayId, r);
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r);
if (stack != null) {
- return stack.mStackId;
+ return stack;
}
}
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display != null) {
for (int i = display.mStacks.size() - 1; i >= 0; --i) {
- stack = display.mStacks.get(i);
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
+ stack = (T) display.mStacks.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
}
}
// TODO: We should create the stack we want on the display at this point.
@@ -2307,6 +2320,8 @@
// Give preference to the stack and display of the input task and activity if they match the
// mode we want to launch into.
+ stack = null;
+ ActivityDisplay display = null;
if (candidateTask != null) {
stack = candidateTask.getStack();
}
@@ -2314,40 +2329,33 @@
stack = r.getStack();
}
if (stack != null) {
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
}
- ActivityDisplay display = stack.getDisplay();
-
- if (display != null) {
- for (int i = display.mStacks.size() - 1; i >= 0; --i) {
- stack = display.mStacks.get(i);
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
- }
- }
- }
+ display = stack.getDisplay();
}
- // Give preference to the type of activity we are trying to launch followed by the windowing
- // mode.
- int stackId = getStackIdForActivityType(activityType);
- if (stackId != INVALID_STACK_ID) {
- return stackId;
+ if (display == null
+ // TODO: Can be removed once we figure-out how non-standard types should launch
+ // outside the default display.
+ || (activityType != ACTIVITY_TYPE_STANDARD
+ && activityType != ACTIVITY_TYPE_UNDEFINED)) {
+ display = getDefaultDisplay();
}
- stackId = getStackIdForWindowingMode(windowingMode);
- if (stackId != INVALID_STACK_ID) {
- return stackId;
+
+ stack = display.getOrCreateStack(windowingMode, activityType, onTop);
+ if (stack != null) {
+ return stack;
}
// Whatever...return some default for now.
if (candidateTask != null && candidateTask.mBounds != null
&& mService.mSupportsFreeformWindowManagement) {
- return FREEFORM_WORKSPACE_STACK_ID;
+ windowingMode = WINDOWING_MODE_FREEFORM;
+ } else {
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
}
- return FULLSCREEN_WORKSPACE_STACK_ID;
+ return display.getOrCreateStack(windowingMode, activityType, onTop);
}
/**
@@ -2364,6 +2372,10 @@
"Display with displayId=" + displayId + " not found.");
}
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ return null;
+ }
+
// Return the topmost valid stack on the display.
for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = activityDisplay.mStacks.get(i);
@@ -2373,11 +2385,9 @@
}
// If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != Display.DEFAULT_DISPLAY) {
- final int newDynamicStackId = getNextStackId();
- if (isValidLaunchStackId(newDynamicStackId, displayId, r)) {
- return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
- }
+ if (displayId != DEFAULT_DISPLAY) {
+ return activityDisplay.createStack(
+ r.getWindowingMode(), r.getActivityType(), true /*onTop*/);
}
Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
@@ -2394,7 +2404,7 @@
case FREEFORM_WORKSPACE_STACK_ID:
return r.supportsFreeform();
case DOCKED_STACK_ID:
- return r.supportsSplitScreen();
+ return r.supportsSplitScreenWindowingMode();
case PINNED_STACK_ID:
return r.supportsPictureInPicture();
case RECENTS_STACK_ID:
@@ -2440,7 +2450,8 @@
final List<ActivityStack> stacks = getActivityDisplayOrCreateLocked(displayId).mStacks;
for (int j = stacks.size() - 1; j >= 0; --j) {
final ActivityStack stack = stacks.get(j);
- if (stack != currentFocus && stack.isFocusable() && stack.shouldBeVisible(null)) {
+ if (stack != currentFocus && stack.isFocusable()
+ && stack.shouldBeVisible(null)) {
return stack;
}
}
@@ -2509,7 +2520,7 @@
return;
}
- final boolean splitScreenActive = getStack(DOCKED_STACK_ID) != null;
+ final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack();
if (!allowResizeInDockedMode
&& !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
// If the docked stack exists, don't resize non-floating stacks independently of the
@@ -2520,7 +2531,7 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
- if (stack.supportSplitScreenWindowingMode()) {
+ if (stack.supportsSplitScreenWindowingMode()) {
if (bounds == null && stack.inSplitScreenWindowingMode()) {
// null bounds = fullscreen windowing mode...at least for now.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2567,25 +2578,31 @@
mResizingTasksDuringAnimation.clear();
}
+ /**
+ * TODO: This should just change the windowing mode and resize vs. actually moving task around.
+ * Can do that once we are no longer using static stack ids. Specially when
+ * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
+ */
private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
- boolean onTop) {
+ int toDisplayId, boolean onTop) {
mWindowManager.deferSurfaceLayout();
try {
- final int fromStackId = fromStack.mStackId;
- if (fromStackId == DOCKED_STACK_ID) {
+ final int windowingMode = fromStack.getWindowingMode();
+ final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
+ final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- final ActivityStack otherStack = getStack(i);
- if (otherStack == null) {
- continue;
- }
+ final ArrayList<ActivityStack> displayStacks = toDisplay.mStacks;
+ for (int i = displayStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = displayStacks.get(i);
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
+ resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
@@ -2594,35 +2611,35 @@
// resize when we remove task from it below and it is detached from the
// display because it no longer contains any tasks.
mAllowDockedStackResize = false;
- } else if (fromStackId == PINNED_STACK_ID) {
- if (onTop) {
- // Log if we are expanding the PiP to fullscreen
- MetricsLogger.action(mService.mContext,
- ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
- }
+ } else if (inPinnedWindowingMode && onTop) {
+ // Log if we are expanding the PiP to fullscreen
+ MetricsLogger.action(mService.mContext,
+ ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
}
- ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
- final boolean isFullscreenStackVisible = fullscreenStack != null
- && fullscreenStack.shouldBeVisible(null);
+
// If we are moving from the pinned stack, then the animation takes care of updating
// the picture-in-picture mode.
- final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID);
+ final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
final int size = tasks.size();
+ final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
+
if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
for (int i = 0; i < size; i++) {
final TaskRecord task = tasks.get(i);
final boolean isTopTask = i == (size - 1);
- if (fromStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode) {
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
// pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(isFullscreenStackVisible ?
- ACTIVITY_TYPE_STANDARD : ACTIVITY_TYPE_HOME);
+ task.setTaskToReturnTo(returnToType);
}
// Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
+ task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ isTopTask /* animate */, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - onTop");
}
@@ -2632,7 +2649,7 @@
// Position the tasks in the fullscreen stack in order at the bottom of the
// stack. Also defer resume until all the tasks have been moved to the
// fullscreen stack.
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, i /* position */,
+ task.reparent(fullscreenStack, i /* position */,
REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - NOT_onTop");
@@ -2648,8 +2665,12 @@
}
void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
- mWindowManager.inSurfaceTransaction(
- () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStack, onTop));
+ moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
+ }
+
+ void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
+ mWindowManager.inSurfaceTransaction(() ->
+ moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
}
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
@@ -2660,7 +2681,7 @@
false /* deferResume */);
}
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -2669,7 +2690,8 @@
return;
}
- final ActivityStack stack = getStack(DOCKED_STACK_ID);
+ final ActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
@@ -2698,13 +2720,14 @@
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
+ final ArrayList<ActivityStack> stacks = getStacksOnDefaultDisplay();
final Rect otherTaskRect = new Rect();
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i == DOCKED_STACK_ID) {
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = stacks.get(i);
+ if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
- final ActivityStack current = getStack(i);
- if (current == null || !current.supportSplitScreenWindowingMode()) {
+ if (!current.supportsSplitScreenWindowingMode()) {
continue;
}
// Need to set windowing mode here before we try to get the dock bounds.
@@ -2714,7 +2737,7 @@
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
- resizeStackLocked(i, !tempRect.isEmpty() ? tempRect : null,
+ resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2731,7 +2754,9 @@
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- final PinnedActivityStack stack = getStack(PINNED_STACK_ID);
+ // TODO(multi-display): Pinned stack display should be passed in.
+ final PinnedActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -2769,32 +2794,14 @@
}
}
- ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- return null;
- }
- return createStack(stackId, activityDisplay, onTop);
-
- }
-
- ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
- switch (stackId) {
- case PINNED_STACK_ID:
- return new PinnedActivityStack(display, stackId, this, onTop);
- default:
- return new ActivityStack(display, stackId, this, onTop);
- }
- }
-
- void removeStackInSurfaceTransaction(int stackId) {
+ private void removeStackInSurfaceTransaction(int stackId) {
final ActivityStack stack = getStack(stackId);
if (stack == null) {
return;
}
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- if (stack.getStackId() == PINNED_STACK_ID) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
/**
* Workaround: Force-stop all the activities in the pinned stack before we reparent them
* to the fullscreen stack. This is to guarantee that when we are removing a stack,
@@ -2827,8 +2834,14 @@
* instead moved back onto the fullscreen stack.
*/
void removeStackLocked(int stackId) {
- mWindowManager.inSurfaceTransaction(
- () -> removeStackInSurfaceTransaction(stackId));
+ mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stackId));
+ }
+
+ /** Removes all stacks in the input windowing mode from the system */
+ void removeStacksInWindowingMode(int windowingMode) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.valueAt(i).removeStacksInWindowingMode(windowingMode);
+ }
}
/**
@@ -2966,11 +2979,11 @@
* @return true if the task has been restored successfully.
*/
boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions) {
- final int stackId = getLaunchStackId(null, aOptions, task);
+ final ActivityStack stack = getLaunchStack(null, aOptions, task, !ON_TOP);
final ActivityStack currentStack = task.getStack();
if (currentStack != null) {
// Task has already been restored once. See if we need to do anything more
- if (currentStack.mStackId == stackId) {
+ if (currentStack == stack) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
@@ -2979,11 +2992,9 @@
currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
}
- final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
-
- stack.addTask(task, false /* toTop */, "restoreRecentTask");
+ stack.addTask(task, !ON_TOP, "restoreRecentTask");
// TODO: move call for creation here and other place into Stack.addTask()
- task.createWindowContainer(false /* toTop */, true /* showForAllUsers */);
+ task.createWindowContainer(!ON_TOP, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -3030,8 +3041,10 @@
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
- ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) {
+ ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
final ActivityStack prevStack = task.getStack();
+ final int stackId = stack.mStackId;
+ final boolean inMultiWindowMode = stack.inMultiWindowMode();
// Check that we aren't reparenting to the same stack that the task is already in
if (prevStack != null && prevStack.mStackId == stackId) {
@@ -3042,22 +3055,22 @@
// Ensure that we aren't trying to move into a multi-window stack without multi-window
// support
- if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
+ if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
throw new IllegalArgumentException("Device doesn't support multi-window, can not"
- + " reparent task=" + task + " to stackId=" + stackId);
+ + " reparent task=" + task + " to stack=" + stack);
}
// Ensure that we're not moving a task to a dynamic stack if device doesn't support
// multi-display.
- // TODO(multi-display): Support non-dynamic stacks on secondary displays.
- if (StackId.isDynamicStack(stackId) && !mService.mSupportsMultiDisplay) {
+ if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
throw new IllegalArgumentException("Device doesn't support multi-display, can not"
+ " reparent task=" + task + " to stackId=" + stackId);
}
// Ensure that we aren't trying to move into a freeform stack without freeform
// support
- if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+ " task=" + task);
}
@@ -3066,25 +3079,25 @@
// used for split-screen mode and will cause things like the docked divider to show up. We
// instead leave the task in its current stack or move it to the fullscreen stack if it
// isn't currently in a stack.
- if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
- stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
+ if (inMultiWindowMode && !task.isResizeable()) {
Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
+ " Moving to stackId=" + stackId + " instead.");
+ // Temporarily disable resizeablility of the task as we don't want it to be resized if,
+ // for example, a docked stack is created which will lead to the stack we are moving
+ // from being resized and and its resizeable tasks being resized.
+ try {
+ task.mTemporarilyUnresizable = true;
+ stack = stack.getDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
+ } finally {
+ task.mTemporarilyUnresizable = false;
+ }
}
-
- // Temporarily disable resizeablility of the task as we don't want it to be resized if, for
- // example, a docked stack is created which will lead to the stack we are moving from being
- // resized and and its resizeable tasks being resized.
- try {
- task.mTemporarilyUnresizable = true;
- return getStack(stackId, CREATE_IF_NEEDED, toTop);
- } finally {
- task.mTemporarilyUnresizable = false;
- }
+ return stack;
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
- final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ final ActivityStack stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException(
"moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
@@ -3114,7 +3127,8 @@
mWindowManager.deferSurfaceLayout();
- PinnedActivityStack stack = getStack(PINNED_STACK_ID);
+ final ActivityDisplay display = r.getStack().getDisplay();
+ PinnedActivityStack stack = display.getPinnedStack();
// This will clear the pinned stack by moving an existing task to the full screen stack,
// ensuring only one task is present.
@@ -3123,7 +3137,7 @@
}
// Need to make sure the pinned stack exist so we can resize it below...
- stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
try {
final TaskRecord task = r.getTask();
@@ -3147,8 +3161,8 @@
moveHomeStackToFront(reason);
}
// Defer resume until below, and do not schedule PiP changes until we animate below
- task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
- DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+ false /* schedulePictureInPictureModeChange */, reason);
} else {
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
@@ -3163,7 +3177,7 @@
r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
// Defer resume until below, and do not schedule PiP changes until we animate below
- newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
}
@@ -3617,7 +3631,7 @@
boolean switchUserLocked(int userId, UserState uss) {
final int focusStackId = mFocusedStack.getStackId();
// We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+ final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenStack();
if (dockedStack != null) {
moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
}
@@ -4057,15 +4071,22 @@
return getActivityDisplayOrCreateLocked(displayId) != null;
}
+ // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
ActivityDisplay getActivityDisplay(int displayId) {
return mActivityDisplays.get(displayId);
}
+ // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+ ActivityDisplay getDefaultDisplay() {
+ return mActivityDisplays.get(DEFAULT_DISPLAY);
+ }
+
/**
* Get an existing instance of {@link ActivityDisplay} or create new if there is a
* corresponding record in display manager.
*/
- private ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
+ // TODO: Look into consolidating with getActivityDisplay()
+ ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
return activityDisplay;
@@ -4290,7 +4311,8 @@
}
final ActivityRecord topActivity = task.getTopActivity();
- if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) {
+ if (launchOnSecondaryDisplayFailed
+ || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
@@ -4304,7 +4326,8 @@
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
- final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+
+ final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenStack();
if (dockedStack != null) {
moveTasksToFullscreenStackLocked(dockedStack,
actualStackId == dockedStack.getStackId());
@@ -4582,13 +4605,10 @@
// Since we don't have an actual source record here, we assume that the currently
// focused activity was the source.
- final ActivityStack focusedStack = getFocusedStack();
- final ActivityRecord sourceRecord = focusedStack != null
- ? focusedStack.topActivity() : null;
- final int stackId = getLaunchStackId(null, activityOptions, task);
+ final ActivityStack stack = getLaunchStack(null, activityOptions, task, ON_TOP);
- if (stackId != INVALID_STACK_ID && task.getStackId() != stackId) {
- task.reparent(stackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+ if (stack != null && task.getStack() != stack) {
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"startActivityFromRecents");
}
@@ -4613,10 +4633,7 @@
}
mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(),
- ActivityManager.START_TASK_TO_FRONT,
- sourceRecord != null
- ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID,
- sourceRecord, task.getStack());
+ ActivityManager.START_TASK_TO_FRONT, task.getStack());
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1a2b46c..9bcba0d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -31,14 +31,15 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ActivityManager.StackId.isDynamicStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
@@ -77,7 +78,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -590,9 +590,7 @@
auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
}
- void postStartActivityProcessing(
- ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
- ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
@@ -614,7 +612,8 @@
}
if (startedActivityStackId == DOCKED_STACK_ID) {
- final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID);
+ final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
if (homeStackVisible) {
// We launch an activity while being in home stack, which means either launcher or
@@ -1002,8 +1001,7 @@
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord,
- mTargetStack);
+ postStartActivityProcessing(r, result, mTargetStack);
return result;
}
@@ -1612,12 +1610,11 @@
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true;
- } else if (launchStack.mStackId == DOCKED_STACK_ID
- || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (launchStack.inSplitScreenWindowingMode()) {
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
- intentTask.reparent(launchStack.mStackId, ON_TOP,
+ intentTask.reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"launchToSide");
} else {
@@ -1634,17 +1631,17 @@
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
- } else if (launchStack.getStackId() == StackId.HOME_STACK_ID
- && mTargetStack.getStackId() != StackId.HOME_STACK_ID) {
+ } else if (launchStack.isActivityTypeHome()
+ && !mTargetStack.isActivityTypeHome()) {
// It is possible for the home activity to be in another stack initially.
// For example, the activity may have been initially started with an intent
// which placed it in the fullscreen stack. To ensure the proper handling of
// the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
@@ -1879,8 +1876,8 @@
if (mTargetStack == null) {
mTargetStack = sourceStack;
} else if (mTargetStack != sourceStack) {
- sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
- !ANIMATE, DEFER_RESUME, "launchToSide");
+ sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, "launchToSide");
}
final TaskRecord topTask = mTargetStack.topTask();
@@ -1977,15 +1974,16 @@
if (mLaunchBounds != null) {
mInTask.updateOverrideConfiguration(mLaunchBounds);
// TODO: Shouldn't we already know what stack to use by the time we get here?
- int stackId = mSupervisor.getLaunchStackId(null, null, mInTask);
- if (stackId != mInTask.getStackId()) {
- mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+ ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+ if (stack != mInTask.getStack()) {
+ mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
- stackId = mInTask.getStackId();
+ stack = mInTask.getStack();
mTargetStack = mInTask.getStack();
}
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+ if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
+ mService.resizeStack(
+ stack.mStackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
}
}
@@ -2116,11 +2114,7 @@
}
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
- final int stackId = task != null ? mSupervisor.getLaunchStackId(r, aOptions, task)
- // TODO: This should go in mSupervisor.getLaunchStackId method...
- : bounds != null && mService.mSupportsFreeformWindowManagement
- ? FREEFORM_WORKSPACE_STACK_ID : FULLSCREEN_WORKSPACE_STACK_ID;
- stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+ stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2143,7 +2137,7 @@
break;
case DOCKED_STACK_ID:
// Any activity which supports split screen can go in the docked stack.
- canUseFocusedStack = r.supportsSplitScreen();
+ canUseFocusedStack = r.supportsSplitScreenWindowingMode();
break;
case FREEFORM_WORKSPACE_STACK_ID:
// Any activity which supports freeform can go in the freeform stack.
@@ -2168,50 +2162,12 @@
return mReuseTask.getStack();
}
- // If the activity is of a specific type, return the associated stack, creating it if
- // necessary
- if (r.isActivityTypeHome()) {
- return mSupervisor.mHomeStack;
- }
- if (r.isActivityTypeRecents()) {
- return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- if (r.isActivityTypeAssistant()) {
- return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
+ final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY;
+ final ActivityStack launchStack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
+ vrDisplayId);
- int launchDisplayId = INVALID_DISPLAY;
- int launchStackId = INVALID_STACK_ID;
- if (aOptions != null) {
- launchDisplayId = aOptions.getLaunchDisplayId();
- final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY;
- launchStackId = mSupervisor.getLaunchStackId(r, aOptions, task, vrDisplayId);
- }
-
- // TODO: Will no longer be needed once we are on longer using static stack ids.
- if (mSupervisor.isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
- return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
- }
- if (launchStackId == DOCKED_STACK_ID) {
- // The preferred launch stack is the docked stack, but it isn't a valid launch stack
- // for this activity, so we put the activity in the fullscreen stack.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- // TODO: Can probably be removed since ASS.getLaunchStackId() does display resolution.
- if (launchDisplayId != INVALID_DISPLAY) {
- // Stack id has higher priority than display id.
- return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
- }
-
- // If we are using Vr2d display, find the virtual display stack.
- // TODO: Can be removed.
- if (mUsingVr2dDisplay) {
- ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r);
- if (DEBUG_STACK) {
- Slog.v(TAG, "Launch stack for app: " + r.toString() +
- ", on virtual display stack:" + as.toString());
- }
- return as;
+ if (launchStack != null) {
+ return launchStack;
}
if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
@@ -2237,13 +2193,15 @@
if (parentStack != null && parentStack.isDockedStack()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
- ON_TOP);
+ final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+ return parentStack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
} else {
// If the parent is not in the docked stack, we check if there is docked window
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
- final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
+ final ActivityStack dockedStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
@@ -2267,15 +2225,6 @@
mWindowManager = wm;
}
- void removePendingActivityLaunchesLocked(ActivityStack stack) {
- for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
- PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
- if (pal.stack == stack) {
- mPendingActivityLaunches.remove(palNdx);
- }
- }
- }
-
static boolean isDocumentLaunchesIntoExisting(int flags) {
return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 7101fc4..5c48f90 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -342,7 +342,7 @@
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack = mStackSupervisor.getStack(DOCKED_STACK_ID);
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
if (stack == null) {
return;
}
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 33f5664..468b867 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
import android.app.RemoteAction;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -34,7 +37,7 @@
PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
boolean onTop) {
- super(display, stackId, supervisor, onTop);
+ super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
}
@Override
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fb8b034..0d8df79 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,20 +18,19 @@
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -573,36 +572,36 @@
/**
* Convenience method to reparent a task to the top or bottom position of the stack.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
- deferResume, true /* schedulePictureInPictureModeChange */, reason);
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
+ true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Convenience method to reparent a task to the top or bottom position of the stack, with
* an option to skip scheduling the picture-in-picture mode change.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ boolean schedulePictureInPictureModeChange, String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
deferResume, schedulePictureInPictureModeChange, reason);
}
- /**
- * Convenience method to reparent a task to a specific position of the stack.
- */
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, position, moveStackMode, animate, deferResume,
+ /** Convenience method to reparent a task to a specific position of the stack. */
+ boolean reparent(ActivityStack preferredStack, int position,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, position, moveStackMode, animate, deferResume,
true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Reparents the task into a preferred stack, creating it if necessary.
*
- * @param preferredStackId the stack id of the target stack to move this task
+ * @param preferredStack the target stack to move this task
* @param position the position to place this task in the new stack
* @param animate whether or not we should wait for the new window created as a part of the
* reparenting to be drawn and animated in
@@ -616,13 +615,16 @@
* @param reason the caller of this reparenting
* @return whether the task was reparented
*/
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
+ // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
+ // re-parenting the task. Can only be done when we are no longer using static stack Ids like
+ /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */
+ boolean reparent(ActivityStack preferredStack, int position,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack();
- final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
+ final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
position == MAX_VALUE);
if (toStack == sourceStack) {
return false;
@@ -705,19 +707,22 @@
toStack.prepareFreezingTaskBounds();
// Make sure the task has the appropriate bounds/size for the stack it is in.
+ final int toStackWindowingMode = toStack.getWindowingMode();
+ final boolean toStackSplitScreenPrimary =
+ toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
&& !Objects.equals(mBounds, toStack.mBounds)) {
kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
deferResume);
- } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
toStack.layoutTaskInStack(this, null);
bounds = mBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
- } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- if (stackId == DOCKED_STACK_ID && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
+ } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
+ if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
// Move recents to front so it is not behind home stack when going into docked
// mode
mService.mStackSupervisor.moveRecentsStackToFront(reason);
@@ -744,11 +749,12 @@
}
// TODO: Handle incorrect request to move before the actual move, not after.
- supervisor.handleNonResizableTaskIfNeeded(this, getWindowingModeForStackId(preferredStackId,
- supervisor.getStack(DOCKED_STACK_ID) != null), DEFAULT_DISPLAY, stackId);
+ final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
+ supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
+ DEFAULT_DISPLAY, stackId);
- boolean successful = (preferredStackId == stackId);
- if (successful && stackId == DOCKED_STACK_ID) {
+ boolean successful = (preferredStack == toStack);
+ if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps(false /* fromHome */);
}
@@ -941,8 +947,8 @@
mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
}
- ActivityStack getStack() {
- return mStack;
+ <T extends ActivityStack> T getStack() {
+ return (T) mStack;
}
/**
@@ -1457,10 +1463,12 @@
return isResizeable(true /* checkSupportsPip */);
}
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// A task can not be docked even if it is considered resizeable because it only supports
// picture-in-picture mode but has a non-resizeable resizeMode
- return mService.mSupportsSplitScreenMultiWindow
+ return super.supportsSplitScreenWindowingMode()
+ && mService.mSupportsSplitScreenMultiWindow
&& (mService.mForceResizableActivities
|| (isResizeable(false /* checkSupportsPip */)
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
@@ -2101,12 +2109,10 @@
return null;
}
- final int stackId = mStack.mStackId;
- if (stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID
- || stackId == ASSISTANT_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || (stackId == DOCKED_STACK_ID && !isResizeable())) {
+ final int windowingMode = getWindowingMode();
+ if (!isActivityTypeStandardOrUndefined()
+ || windowingMode == WINDOWING_MODE_FULLSCREEN
+ || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
return isResizeable() ? mStack.mBounds : null;
} else if (!getWindowConfiguration().persistTaskBounds()) {
return mStack.mBounds;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index dc5a8144..9e028d3 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -21,8 +21,10 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -152,6 +154,17 @@
onOverrideConfigurationChanged(mTmpConfig);
}
+ /**
+ * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
+ * with another activity.
+ */
+ public boolean inMultiWindowMode() {
+ /*@WindowConfiguration.WindowingMode*/ int windowingMode =
+ mFullConfiguration.windowConfiguration.getWindowingMode();
+ return windowingMode != WINDOWING_MODE_FULLSCREEN
+ && windowingMode != WINDOWING_MODE_UNDEFINED;
+ }
+
/** Returns true if this container is currently in split-screen windowing mode. */
public boolean inSplitScreenWindowingMode() {
/*@WindowConfiguration.WindowingMode*/ int windowingMode =
@@ -175,7 +188,7 @@
* {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on
* its current state.
*/
- public boolean supportSplitScreenWindowingMode() {
+ public boolean supportsSplitScreenWindowingMode() {
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
}
@@ -238,6 +251,16 @@
/**
* Returns true if this container is compatible with the input windowing mode and activity type.
+ * The container is compatible:
+ * - If {@param activityType} and {@param windowingMode} match this container activity type and
+ * windowing mode.
+ * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
+ * standard or undefined and its windowing mode matches {@param windowingMode}.
+ * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
+ * also standard or undefined and its activity type matches {@param activityType} regardless of
+ * if {@param windowingMode} matches the containers windowing mode.
*/
public boolean isCompatible(int windowingMode, int activityType) {
final int thisActivityType = getActivityType();
@@ -249,7 +272,8 @@
return true;
}
- if (activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD) {
+ if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
+ || !isActivityTypeStandardOrUndefined()) {
// Only activity type need to match for non-standard activity types that are defined.
return sameActivityType;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 526f815..679be1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -16,9 +16,15 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -26,14 +32,13 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -46,64 +51,53 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private static final int TEST_STACK_ID = 100;
-
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
private final ComponentName secondaryActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity2");
+
+ private ActivityManagerService mService;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+ private ActivityRecord mActivity;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ mActivity = createActivity(mService, testActivityComponent, mTask);
+ }
+
@Test
public void testStackCleanupOnClearingTask() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- record.setTask(null);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
+ mActivity.setTask(null);
+ assertEquals(getActivityRemovedFromStackCount(), 1);
}
@Test
public void testStackCleanupOnActivityRemoval() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- task.removeActivity(record);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
+ mTask.removeActivity(mActivity);
+ assertEquals(getActivityRemovedFromStackCount(), 1);
}
@Test
public void testStackCleanupOnTaskRemoval() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- service.mStackSupervisor.getStack(TEST_STACK_ID)
- .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
-
+ mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
// Stack should be gone on task removal.
- assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID));
+ assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
}
@Test
public void testNoCleanupMovingActivityInSameStack() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, oldTask);
- final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID);
-
- record.reparent(newTask, 0, null /*reason*/);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0);
- }
-
- private static int getActivityRemovedFromStackCount(ActivityManagerService service,
- int stackId) {
- final ActivityStack stack = service.mStackSupervisor.getStack(stackId);
- if (stack instanceof ActivityStackReporter) {
- return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount();
- }
-
- return -1;
+ final TaskRecord newTask =
+ createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ mActivity.reparent(newTask, 0, null /*reason*/);
+ assertEquals(getActivityRemovedFromStackCount(), 0);
}
@Test
@@ -126,16 +120,21 @@
private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
float aspectRatio, Rect expectedActivityBounds) {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
// Verify with nav bar on the right.
- when(service.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
- task.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
- record.info.maxAspectRatio = aspectRatio;
- record.ensureActivityConfigurationLocked(0 /* globalChanges */, false /* preserveWindow */);
- assertEquals(expectedActivityBounds, record.getBounds());
+ when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+ mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
+ mActivity.info.maxAspectRatio = aspectRatio;
+ mActivity.ensureActivityConfigurationLocked(
+ 0 /* globalChanges */, false /* preserveWindow */);
+ assertEquals(expectedActivityBounds, mActivity.getBounds());
+ }
+
+ private int getActivityRemovedFromStackCount() {
+ if (mStack instanceof ActivityStackReporter) {
+ return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount();
+ }
+
+ return -1;
}
@@ -156,26 +155,22 @@
private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
boolean activityResizeable, boolean expected) {
- final ActivityManagerService service = createActivityManagerService();
- service.mSupportsMultiWindow = true;
-
+ mService.mSupportsMultiWindow = true;
final TaskRecord task = taskPresent
- ? createTask(service, testActivityComponent, TEST_STACK_ID) : null;
+ ? createTask(mService.mStackSupervisor, testActivityComponent, mStack) : null;
if (task != null) {
- task.setResizeMode(taskResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
- : ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
+ task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
}
- final ActivityRecord record = createActivity(service, secondaryActivityComponent,
- task);
- record.info.resizeMode = activityResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
- : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ final ActivityRecord record = createActivity(mService, secondaryActivityComponent, task);
+ record.info.resizeMode = activityResizeable
+ ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
- record.canBeLaunchedOnDisplay(Display.DEFAULT_DISPLAY);
+ record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
- assertEquals(((TestActivityStackSupervisor) service.mStackSupervisor)
+ assertEquals(((TestActivityStackSupervisor) mService.mStackSupervisor)
.getLastResizeableFromCanPlaceEntityOnDisplay(), expected);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index cd1843b..6b93d0e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -16,11 +16,10 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -33,6 +32,7 @@
import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
@@ -54,6 +54,21 @@
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
+ private ActivityManagerService mService;
+ private ActivityStackSupervisor mSupervisor;
+ private ActivityStack mFullscreenStack;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ }
+
/**
* This test ensures that we do not try to restore a task based off an invalid task id. The
* stack supervisor is a test version so there will be no tasks present. We should expect
@@ -61,8 +76,7 @@
*/
@Test
public void testRestoringInvalidTask() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- TaskRecord task = service.mStackSupervisor.anyTaskForIdLocked(0 /*taskId*/,
+ TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null);
assertNull(task);
}
@@ -73,43 +87,44 @@
*/
@Test
public void testReplacingTaskInPinnedStack() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord firstTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+ final TaskRecord firstTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
firstTask);
// Create a new task on the full screen stack
- final TaskRecord secondTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord secondActivity = createActivity(service, testActivityComponent,
+ final TaskRecord secondTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord secondActivity = createActivity(mService, testActivityComponent,
secondTask);
- service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack",
- service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID));
+ mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
// Ensure full screen stack has both tasks.
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask,
- secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
// Move first activity to pinned stack.
- service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity,
- new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove");
+ final Rect sourceBounds = new Rect();
+ mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
+ 0f /*aspectRatio*/, false, "initialMove");
+ final ActivityDisplay display = mFullscreenStack.getDisplay();
+ ActivityStack pinnedStack = display.getPinnedStack();
// Ensure a task has moved over.
- ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask);
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask);
+ ensureStackPlacement(pinnedStack, firstTask);
+ ensureStackPlacement(mFullscreenStack, secondTask);
// Move second activity to pinned stack.
- service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity,
- new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove");
+ mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
+ 0f /*aspectRatio*/, false, "secondMove");
+
+ // Need to get pinned stack again as a new instance might have been created.
+ pinnedStack = display.getPinnedStack();
// Ensure stacks have swapped tasks.
- ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask);
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask);
+ ensureStackPlacement(pinnedStack, secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask);
}
- private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId,
- TaskRecord... tasks) {
- final ActivityStack stack = supervisor.getStack(stackId);
+ private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
@@ -127,15 +142,14 @@
*/
@Test
public void testStoppingActivityRemovedWhenResumed() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord firstTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+ final TaskRecord firstTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
firstTask);
- service.mStackSupervisor.mStoppingActivities.add(firstActivity);
+ mSupervisor.mStoppingActivities.add(firstActivity);
firstActivity.completeResumeLocked();
- assertFalse(service.mStackSupervisor.mStoppingActivities.contains(firstActivity));
+ assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 02fba08..4ee1f47 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -16,12 +16,14 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -30,6 +32,7 @@
import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -48,78 +51,80 @@
private static final ComponentName testOverlayComponent =
ComponentName.unflattenFromString("com.foo/.OverlayActivity");
+ private ActivityManagerService mService;
+ private ActivityStackSupervisor mSupervisor;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = createTask(mSupervisor, testActivityComponent, mStack);
+ }
+
@Test
public void testEmptyTaskCleanupOnRemove() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- assertNotNull(task.getWindowContainerController());
- service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
- "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
- assertNull(task.getWindowContainerController());
+ assertNotNull(mTask.getWindowContainerController());
+ mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+ assertNull(mTask.getWindowContainerController());
}
@Test
public void testOccupiedTaskCleanupOnRemove() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- assertNotNull(task.getWindowContainerController());
- service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
- "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
- assertNotNull(task.getWindowContainerController());
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ assertNotNull(mTask.getWindowContainerController());
+ mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+ assertNotNull(mTask.getWindowContainerController());
}
@Test
public void testNoPauseDuringResumeTopActivity() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
// Simulate the a resumed activity set during
// {@link ActivityStack#resumeTopActivityUncheckedLocked}.
- service.mStackSupervisor.inResumeTopActivity = true;
- testStack.mResumedActivity = activityRecord;
+ mSupervisor.inResumeTopActivity = true;
+ mStack.mResumedActivity = r;
- final boolean waiting = testStack.goToSleepIfPossible(false);
+ final boolean waiting = mStack.goToSleepIfPossible(false);
// Ensure we report not being ready for sleep.
assertFalse(waiting);
// Make sure the resumed activity is untouched.
- assertEquals(testStack.mResumedActivity, activityRecord);
+ assertEquals(mStack.mResumedActivity, r);
}
@Test
public void testStopActivityWhenActivityDestroyed() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- activityRecord.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
- service.mStackSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", testStack);
-
- testStack.stopActivityLocked(activityRecord);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
+ mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
+ mStack.stopActivityLocked(r);
+ // Mostly testing to make sure there is a crash in the call part, so if we get here we are
+ // good-to-go!
}
@Test
public void testFindTaskWithOverlay() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task,
- 0);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask, 0);
// Overlay must be for a different user to prevent recognizing a matching top activity
- final ActivityRecord taskOverlay = createActivity(service, testOverlayComponent, task,
+ final ActivityRecord taskOverlay = createActivity(mService, testOverlayComponent, mTask,
UserHandle.PER_USER_RANGE * 2);
taskOverlay.mTaskOverlay = true;
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
final ActivityStackSupervisor.FindTaskResult result =
new ActivityStackSupervisor.FindTaskResult();
- testStack.findTaskLocked(activityRecord, result);
+ mStack.findTaskLocked(r, result);
- assertEquals(task.getTopActivity(false /* includeOverlays */), activityRecord);
- assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
+ assertEquals(mTask.getTopActivity(false /* includeOverlays */), r);
+ assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
assertNotNull(result.r);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 6caa60b..b4bfa62 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,7 +16,7 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doReturn;
@@ -76,16 +76,6 @@
return service;
}
- protected static ActivityStack createActivityStack(ActivityManagerService service,
- int stackId, int displayId, boolean onTop) {
- if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
- return ((TestActivityStackSupervisor) service.mStackSupervisor)
- .createTestStack(stackId, onTop);
- }
-
- return null;
- }
-
protected static ActivityRecord createActivity(ActivityManagerService service,
ComponentName component, TaskRecord task) {
return createActivity(service, component, task, 0 /* userId */);
@@ -114,8 +104,8 @@
return activity;
}
- protected static TaskRecord createTask(ActivityManagerService service,
- ComponentName component, int stackId) {
+ protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
+ ComponentName component, ActivityStack stack) {
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = component.getPackageName();
@@ -123,11 +113,9 @@
Intent intent = new Intent();
intent.setComponent(component);
- final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
+ final TaskRecord task = new TaskRecord(supervisor.mService, 0, aInfo, intent /*intent*/,
null /*_taskDescription*/);
- final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
- true /*createStaticStackIfNeeded*/, true /*onTop*/);
- service.mStackSupervisor.setFocusStackUnchecked("test", stack);
+ supervisor.setFocusStackUnchecked("test", stack);
stack.addTask(task, true, "creating test task");
task.setStack(stack);
task.setWindowContainerController(mock(TaskWindowContainerController.class));
@@ -135,16 +123,18 @@
return task;
}
-
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
protected static class TestActivityManagerService extends ActivityManagerService {
- public TestActivityManagerService(Context context) {
+ TestActivityManagerService(Context context) {
super(context);
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
+ mSupportsSplitScreenMultiWindow = true;
+ mSupportsFreeformWindowManagement = true;
+ mSupportsPictureInPicture = true;
mWindowManager = WindowTestUtils.getWindowManagerService(context);
}
@@ -171,10 +161,15 @@
mDisplayManager =
(DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
- mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+ mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
attachDisplay(mDisplay);
}
+ @Override
+ ActivityDisplay getDefaultDisplay() {
+ return mDisplay;
+ }
+
// TODO: Use Mockito spy instead. Currently not possible due to TestActivityStackSupervisor
// access to ActivityDisplay
@Override
@@ -218,41 +213,36 @@
boolean preserveWindows) {
}
- <T extends ActivityStack> T createTestStack(int stackId, boolean onTop) {
- return (T) createStack(stackId, mDisplay, onTop);
+ // Always keep things awake
+ @Override
+ boolean hasAwakeDisplay() {
+ return true;
+ }
+ }
+
+ private static class TestActivityDisplay extends ActivityDisplay {
+
+ private final ActivityStackSupervisor mSupervisor;
+ TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
+ super(supervisor, displayId);
+ mSupervisor = supervisor;
}
@Override
- ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
- if (stackId == PINNED_STACK_ID) {
- return new PinnedActivityStack(display, stackId, this, onTop) {
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
};
} else {
- return new TestActivityStack(display, stackId, this, onTop);
+ return (T) new TestActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
}
-
- @Override
- protected <T extends ActivityStack> T getStack(int stackId,
- boolean createStaticStackIfNeeded, boolean createOnTop) {
- final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop);
-
- if (stack != null || !createStaticStackIfNeeded) {
- return stack;
- }
-
- return createTestStack(stackId, createOnTop);
- }
-
- // Always keep things awake
- @Override
- boolean hasAwakeDisplay() {
- return true;
- }
}
private static WindowManagerService prepareMockWindowManager() {
@@ -282,9 +272,10 @@
extends ActivityStack<T> implements ActivityStackReporter {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
+
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
- boolean onTop) {
- super(display, stackId, supervisor, onTop);
+ int windowingMode, int activityType, boolean onTop) {
+ super(display, stackId, supervisor, windowingMode, activityType, onTop);
}
@Override
@@ -310,36 +301,4 @@
return mContainerController;
}
}
-
-
- protected static class ActivityStackBuilder {
- private boolean mOnTop = true;
- private int mStackId = 0;
- private int mDisplayId = 1;
-
- private final ActivityManagerService mService;
-
- public ActivityStackBuilder(ActivityManagerService ams) {
- mService = ams;
- }
-
- public ActivityStackBuilder setOnTop(boolean onTop) {
- mOnTop = onTop;
- return this;
- }
-
- public ActivityStackBuilder setStackId(int id) {
- mStackId = id;
- return this;
- }
-
- public ActivityStackBuilder setDisplayId(int id) {
- mDisplayId = id;
- return this;
- }
-
- public ActivityStack build() {
- return createActivityStack(mService, mStackId, mDisplayId, mOnTop);
- }
- }
}