Support for setting stack windowing mode
We currently create a in stack in the windowing mode we want anytime we
want to change the windowing mode of a task which isn't optimal.
We now change the current stack windowing mode for some cases vs.
creating a new stack.
Test: go/wm-smoke
Bug: 64146578
Fixes: 68948655
Fixes: 65739235
Fixes: 29068935
Change-Id: I9fceb7d608e86610c1fb5ba59b15087a96e7401e
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index ea7b443..2289f85 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -276,17 +276,8 @@
if (windowingMode == WINDOWING_MODE_PINNED) {
return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
}
- final T stack = (T) new ActivityStack(
+ return (T) new ActivityStack(
this, stackId, mSupervisor, windowingMode, activityType, onTop);
-
- if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Make sure recents stack exist when creating a dock stack as it normally needs 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;
}
/**
@@ -365,6 +356,7 @@
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
+ onSplitScreenModeActivated();
}
}
@@ -377,6 +369,42 @@
mPinnedStack = null;
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
+ // Inform the reset of the system that split-screen mode was dismissed so things like
+ // resizing all the other stacks can take place.
+ onSplitScreenModeDismissed();
+ }
+ }
+
+ private void onSplitScreenModeDismissed() {
+ mSupervisor.mWindowManager.deferSurfaceLayout();
+ try {
+ // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = mStacks.get(i);
+ if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
+ continue;
+ }
+ otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ } finally {
+ mSupervisor.mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ private void onSplitScreenModeActivated() {
+ mSupervisor.mWindowManager.deferSurfaceLayout();
+ try {
+ // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = mStacks.get(i);
+ if (otherStack == mSplitScreenPrimaryStack
+ || !otherStack.affectedBySplitScreenResize()) {
+ continue;
+ }
+ otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+ } finally {
+ mSupervisor.mWindowManager.continueSurfaceLayout();
}
}
@@ -475,22 +503,10 @@
supportsFreeform, supportsPip, activityType)) {
return windowingMode;
}
- // Return the display's windowing mode
- return getWindowingMode();
- }
-
- /** 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;
+ // Try to use the display's windowing mode otherwise fallback to fullscreen.
+ windowingMode = getWindowingMode();
+ return windowingMode != WINDOWING_MODE_UNDEFINED
+ ? windowingMode : WINDOWING_MODE_FULLSCREEN;
}
/**
@@ -599,7 +615,20 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "displayId=" + mDisplayId + " mStacks=" + mStacks);
+ pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
+ final String myPrefix = prefix + " ";
+ if (mHomeStack != null) {
+ pw.println(myPrefix + "mHomeStack=" + mHomeStack);
+ }
+ if (mRecentsStack != null) {
+ pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
+ }
+ if (mPinnedStack != null) {
+ pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
+ }
+ if (mSplitScreenPrimaryStack != null) {
+ pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
+ }
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 02ba6a0..0ee1e9a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4755,7 +4755,7 @@
throws RemoteException {
Slog.i(TAG, "Activity tried to startVoiceInteraction");
synchronized (this) {
- ActivityRecord activity = getFocusedStack().topActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
@@ -10431,13 +10431,7 @@
"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);
- // TODO: Should just change windowing mode vs. re-parenting...
- r.getTask().reparent(fullscreenStack, ON_TOP,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode");
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10446,6 +10440,11 @@
@Override
public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+ toTop, ANIMATE, null /* initialBounds */);
+ return;
+ }
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
@@ -10458,20 +10457,16 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ " to windowingMode=" + windowingMode + " toTop=" + toTop);
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateState(SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- null /* initialBounds */);
- }
if (!task.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task "
- + taskId + " to non-standard windowin mode=" + windowingMode);
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to windowing mode="
+ + windowingMode);
}
final ActivityDisplay display = task.getStack().getDisplay();
final ActivityStack stack = display.getOrCreateStack(windowingMode,
task.getStack().getActivityType(), toTop);
- // TODO: We should just change the windowing mode for the task vs. creating and
- // moving it to a stack.
+ // TODO: Use ActivityStack.setWindowingMode instead of re-parenting.
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -10480,6 +10475,55 @@
}
}
+ /**
+ * Moves the specified task to the primary-split-screen stack.
+ *
+ * @param taskId Id of task to move.
+ * @param createMode The mode the primary split screen stack should be created in if it doesn't
+ * exist already. See
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+ * and
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * @param toTop If the task and stack should be moved to the top.
+ * @param animate Whether we should play an animation for the moving the task.
+ * @param initialBounds If the primary stack gets created, it will use these bounds for the
+ * stack. Pass {@code null} to use default bounds.
+ */
+ @Override
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ boolean animate, Rect initialBounds) {
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "setTaskWindowingModeSplitScreenPrimary()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+ return false;
+ }
+ if (DEBUG_STACK) Slog.d(TAG_STACK,
+ "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+ + " to createMode=" + createMode + " toTop=" + toTop);
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to split-screen windowing mode");
+ }
+
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ final int windowingMode = task.getWindowingMode();
+ final ActivityStack stack = task.getStack();
+ if (toTop) {
+ stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
+ }
+ stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate);
+ return windowingMode != task.getWindowingMode();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
@@ -10517,58 +10561,6 @@
}
/**
- * Moves the input task to the primary-split-screen stack.
- *
- * @param taskId Id of task to move.
- * @param createMode The mode the primary split screen stack should be created in if it doesn't
- * exist already. See
- * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
- * and
- * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
- * @param toTop If the task and stack should be moved to the top.
- * @param animate Whether we should play an animation for the moving the task
- * @param initialBounds If the primary stack gets created, it will use these bounds for the
- * stack. Pass {@code null} to use default bounds.
- */
- @Override
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
- boolean animate, Rect initialBounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
- "setTaskWindowingModeSplitScreenPrimary()");
- synchronized (this) {
- long ident = Binder.clearCallingIdentity();
- try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task == null) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
- return false;
- }
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: 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
- // TODO: Should just change windowing mode vs. re-parenting...
- final boolean moved = task.reparent(stack, toTop,
- REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
- "setTaskWindowingModeSplitScreenPrimary");
- if (moved) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- return moved;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- /**
* Dismisses split-screen multi-window mode.
* @param toTop If true the current primary split-screen stack will be placed or left on top.
*/
@@ -10584,14 +10576,11 @@
Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
return;
}
+
if (toTop) {
- mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- true /* preserveWindows */, true /* allowResizeInDockedMode */,
- !DEFER_RESUME);
- } else {
- mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */);
+ stack.moveToFront("dismissSplitScreenMode");
}
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12947,7 +12936,7 @@
return false;
}
- final ActivityRecord activity = focusedStack.topActivity();
+ final ActivityRecord activity = focusedStack.getTopActivity();
if (activity == null) {
return false;
}
@@ -12964,7 +12953,7 @@
try {
synchronized (this) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = getFocusedStack().topActivity();
+ ActivityRecord top = getFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
@@ -13007,7 +12996,7 @@
"enqueueAssistContext()");
synchronized (this) {
- ActivityRecord activity = getFocusedStack().topActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8cc584e..cfb1035 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+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_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;
@@ -95,7 +98,6 @@
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -345,6 +347,7 @@
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
+ private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
@@ -452,8 +455,8 @@
mStackId = stackId;
mCurrentUser = mService.mUserController.getCurrentUserId();
mTmpRect2.setEmpty();
- setWindowingMode(windowingMode);
setActivityType(activityType);
+ setWindowingMode(windowingMode);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -479,6 +482,125 @@
}
@Override
+ public void setWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode, false /* animate */);
+ }
+
+ void setWindowingMode(int preferredWindowingMode, boolean animate) {
+ final int currentMode = getWindowingMode();
+ final ActivityDisplay display = getDisplay();
+ final TaskRecord topTask = topTask();
+ final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
+ mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
+
+ // Need to make sure windowing mode is supported.
+ int windowingMode = display.resolveWindowingMode(
+ null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());;
+ if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ // Resolution to split-screen secondary for the primary split-screen stack means we want
+ // to go fullscreen.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ // Take any required action due to us not supporting the preferred windowing mode.
+ if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
+ if (display.hasSplitScreenPrimaryStack()
+ && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
+ // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
+ // and display a warning toast about it.
+ mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ if (currentMode == windowingMode) {
+ // You are already in the window mode silly...
+ return;
+ }
+
+ final WindowManagerService wm = mService.mWindowManager;
+ final ActivityRecord topActivity = getTopActivity();
+
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN && topActivity != null
+ && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) {
+ // Inform the user that they are starting an app that may not work correctly in
+ // multi-window mode.
+ final String packageName = topActivity.appInfo.packageName;
+ mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+ topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+ }
+
+ wm.deferSurfaceLayout();
+ try {
+ if (!animate && topActivity != null) {
+ mNoAnimActivities.add(topActivity);
+ }
+ super.setWindowingMode(windowingMode);
+
+ if (mWindowContainerController == null) {
+ // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
+ return;
+ }
+
+ if (windowingMode == WINDOWING_MODE_PINNED || currentMode == WINDOWING_MODE_PINNED) {
+ // TODO: Need to remove use of PinnedActivityStack for this to be supported.
+ // NOTE: Need to ASS.scheduleUpdatePictureInPictureModeIfNeeded() in
+ // setWindowModeUnchecked() when this support is added. See TaskRecord.reparent()
+ throw new IllegalArgumentException(
+ "Changing pinned windowing mode not currently supported");
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+ // We already have a split-screen stack in this display, so just move the tasks over.
+ // TODO: Figure-out how to do all the stuff in
+ // AMS.setTaskWindowingModeSplitScreenPrimary
+ throw new IllegalArgumentException("Setting primary split-screen windowing mode"
+ + " while there is already one isn't currently supported");
+ //return;
+ }
+
+ mTmpRect2.setEmpty();
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
+ mWindowContainerController.getRawBounds(mTmpRect2);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (topTask != null) {
+ // TODO: Can we consolidate this and other sites that call this methods?
+ Rect bounds = topTask().getLaunchBounds();
+ if (bounds != null) {
+ mTmpRect2.set(bounds);
+ }
+ }
+ }
+ }
+
+ if (!Objects.equals(mBounds, mTmpRect2)) {
+ resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+ }
+ } finally {
+ if (mDisplayId == DEFAULT_DISPLAY
+ && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally needs to
+ // be on the other side of the docked stack and we make visibility decisions based
+ // on that.
+ // TODO: This is only here to help out with the case where recents stack doesn't
+ // exist yet. For that case the initial size of the split-screen stack will be the
+ // the one where the home stack is visible since recents isn't visible yet, but the
+ // divider will be off. I think we should just make the initial bounds that of home
+ // so that the divider matches and remove this logic.
+ display.getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ // If task moved to docked stack - show recents if needed.
+ mService.mWindowManager.showRecentApps(false /* fromHome */);
+ }
+ wm.continueSurfaceLayout();
+ }
+
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+
+ @Override
public boolean isCompatible(int windowingMode, int activityType) {
// TODO: Should we just move this to ConfigurationContainer?
if (activityType == ACTIVITY_TYPE_UNDEFINED) {
@@ -538,12 +660,6 @@
* either destroyed completely or re-parented.
*/
private void removeFromDisplay() {
- if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // If we removed a docked stack we want to resize it so it resizes all other stacks
- // in the system to fullscreen.
- mStackSupervisor.resizeDockedStackLocked(
- null, null, null, null, null, PRESERVE_WINDOWS);
- }
final ActivityDisplay display = getDisplay();
if (display != null) {
display.removeChild(this);
@@ -730,14 +846,11 @@
return null;
}
- final ActivityRecord topActivity() {
+ ActivityRecord getTopActivity() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (!r.finishing) {
- return r;
- }
+ final ActivityRecord r = mTaskHistory.get(taskNdx).getTopActivity();
+ if (r != null) {
+ return r;
}
}
return null;
@@ -751,7 +864,7 @@
return null;
}
- final TaskRecord bottomTask() {
+ private TaskRecord bottomTask() {
if (mTaskHistory.isEmpty()) {
return null;
}
@@ -1607,8 +1720,6 @@
}
final int otherWindowingMode = other.getWindowingMode();
- // TODO: Can be removed once we are no longer using returnToType for back functionality
- final ActivityStack stackBehind = i > 0 ? display.getChildAt(i - 1) : null;
if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
if (other.isStackTranslucent(starting)) {
@@ -1802,6 +1913,13 @@
return inPinnedWindowingMode();
}
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
+ final TaskRecord topTask = topTask();
+ return super.supportsSplitScreenWindowingMode()
+ && (topTask == null || topTask.supportsSplitScreenWindowingMode());
+ }
+
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
boolean affectedBySplitScreenResize() {
if (!supportsSplitScreenWindowingMode()) {
@@ -4299,7 +4417,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
- final ActivityRecord topActivity = topStack != null ? topStack.topActivity() : null;
+ final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
if (numTasks == 0 || index < 0) {
@@ -4502,6 +4620,7 @@
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
if (inFreeformWindowingMode()) {
+ // TODO: Can be removed now since each freeform task is in its own stack.
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 062083c..41695fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2945,6 +2945,7 @@
* 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.
*/
+ // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode()
ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
final ActivityStack prevStack = task.getStack();
final int stackId = stack.mStackId;
@@ -2971,8 +2972,7 @@
+ " reparent task=" + task + " to stackId=" + stackId);
}
- // Ensure that we aren't trying to move into a freeform stack without freeform
- // support
+ // Ensure that we aren't trying to move into a freeform stack without freeform support
if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
@@ -3376,7 +3376,7 @@
// When launching tasks behind, update the last active time of the top task after the new
// task has been shown briefly
- final ActivityRecord top = stack.topActivity();
+ final ActivityRecord top = stack.getTopActivity();
if (top != null) {
top.getTask().touchActiveTime();
}
@@ -4200,10 +4200,9 @@
}
}
- final ActivityRecord topActivity = task.getTopActivity();
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
- // Display a warning toast that we tried to put a non-dockable task in the docked
- // stack.
+ // Display a warning toast that we tried to put an app that doesn't support split-screen
+ // in split-screen.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
@@ -4217,6 +4216,7 @@
return;
}
+ final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
@@ -4552,7 +4552,7 @@
final ActivityStack stack = display.getChildAt(j);
// Get top activity from a visible stack and add it to the list.
if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.topActivity();
+ final ActivityRecord top = stack.getTopActivity();
if (top != null) {
if (stack == mFocusedStack) {
topActivityTokens.add(0, top.appToken);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1c80282..32d5ba8 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1119,7 +1119,7 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
- final ActivityRecord topFocused = topStack.topActivity();
+ final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
@@ -1565,8 +1565,8 @@
&& (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
&& !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (mSourceRecord == null || (mSourceStack.topActivity() != null &&
- mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) {
+ if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
+ mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
@@ -1956,7 +1956,7 @@
if (mDoResume) {
mTargetStack.moveToFront("addingToTopTask");
}
- final ActivityRecord prev = mTargetStack.topActivity();
+ final ActivityRecord prev = mTargetStack.getTopActivity();
final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
mIntent, null, null, true);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 40b1cac..949f51f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -606,9 +606,8 @@
final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
- final boolean mightReplaceWindow =
- replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode)
- && topActivity != null;
+ final boolean mightReplaceWindow = topActivity != null
+ && replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode);
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
@@ -722,7 +721,6 @@
}
// TODO: Handle incorrect request to move before the actual move, not after.
- final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenPrimaryStack();
supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
DEFAULT_DISPLAY, toStack);
@@ -735,10 +733,9 @@
}
/**
- * Returns true if the windows of tasks being moved to the target stack from the source
- * stack should be replaced, meaning that window manager will keep the old window around
- * until the new is ready.
- * @hide
+ * @return True if the windows of tasks being moved to the target stack from the source stack
+ * should be replaced, meaning that window manager will keep the old window around until the new
+ * is ready.
*/
private static boolean replaceWindowsOnTaskMove(
int sourceWindowingMode, int targetWindowingMode) {
@@ -2062,11 +2059,8 @@
}
static Rect validateBounds(Rect bounds) {
- if (bounds != null && bounds.isEmpty()) {
- Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());
- return null;
- }
- return bounds;
+ // TODO: Not needed once we have bounds in WindowConfiguration.
+ return (bounds != null && bounds.isEmpty()) ? null : bounds;
}
/** Updates the task's bounds and override configuration to match what is expected for the
@@ -2095,7 +2089,7 @@
}
/** Returns the bounds that should be used to launch this task. */
- private Rect getLaunchBounds() {
+ Rect getLaunchBounds() {
if (mStack == null) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c4b810f..4d839d0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -1488,7 +1489,7 @@
* Returns the topmost stack on the display that is compatible with the input windowing mode.
* Null is no compatible stack on the display.
*/
- TaskStack getStack(int windowingMode) {
+ TaskStack getTopStackInWindowingMode(int windowingMode) {
return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
}
@@ -1764,10 +1765,6 @@
final TaskStack stack = new TaskStack(mService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
-
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
- }
return stack;
}
@@ -2274,7 +2271,7 @@
/** Returns true if the stack in the windowing mode is visible. */
boolean isStackVisible(int windowingMode) {
- final TaskStack stack = getStack(windowingMode);
+ final TaskStack stack = getTopStackInWindowingMode(windowingMode);
return stack != null && stack.isVisible();
}
@@ -3391,6 +3388,12 @@
}
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ if (activityType == ACTIVITY_TYPE_UNDEFINED
+ && windowingMode == stack.getWindowingMode()) {
+ // Passing in undefined type means we want to match the topmost stack with the
+ // windowing mode.
+ return stack;
+ }
if (stack.isCompatible(windowingMode, activityType)) {
return stack;
}
@@ -3461,6 +3464,7 @@
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
+ mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
@@ -3471,6 +3475,10 @@
mPinnedStack = null;
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
+ // Re-set the split-screen create mode whenever the split-screen stack is removed.
+ mService.setDockedStackCreateStateLocked(
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+ mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index e693e5a..d79ba89 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -559,7 +559,7 @@
void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
// TODO: Maybe only allow split-screen windowing modes?
final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
- ? mDisplayContent.getStack(targetWindowingMode)
+ ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
: null;
final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
@@ -663,11 +663,15 @@
if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
return;
}
- final TaskStack fullscreenStack = mDisplayContent.getStack(
+ final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
- final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
- setMinimizedDockedStack(homeVisible && !homeBehind, animate);
+ boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
+ if (homeVisible && topSecondaryStack != null) {
+ // Home should only be considered visible if it is greater or equal to the top secondary
+ // stack in terms of z-order.
+ homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
+ }
+ setMinimizedDockedStack(homeVisible, animate);
}
private boolean isWithinDisplay(Task task) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index aff1bc6..95c1d53 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -225,11 +225,13 @@
}
}
- private void getRawBounds(Rect outBounds) {
- if (mContainer.getRawFullscreen()) {
- outBounds.setEmpty();
- } else {
- mContainer.getRawBounds(outBounds);
+ public void getRawBounds(Rect outBounds) {
+ synchronized (mWindowMap) {
+ if (mContainer.getRawFullscreen()) {
+ outBounds.setEmpty();
+ } else {
+ mContainer.getRawBounds(outBounds);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index dde7946..053fb47 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -685,9 +685,12 @@
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
super.onConfigurationChanged(newParentConfig);
- if (mDisplayContent != null && prevWindowingMode != getWindowingMode()) {
- mDisplayContent.onStackWindowingModeChanged(this);
+ final int windowingMode = getWindowingMode();
+ if (mDisplayContent == null || prevWindowingMode == windowingMode) {
+ return;
}
+ mDisplayContent.onStackWindowingModeChanged(this);
+ updateBoundsForWindowModeChange();
}
@Override
@@ -699,39 +702,41 @@
mDisplayContent = dc;
mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
"animation background stackId=" + mStackId);
+ updateBoundsForWindowModeChange();
+ super.onDisplayChanged(dc);
+ }
+ private void updateBoundsForWindowModeChange() {
Rect bounds = null;
- final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
- if (inSplitScreenPrimaryWindowingMode()
- || (dockedStack != null && inSplitScreenSecondaryWindowingMode()
- && !dockedStack.fillsParent())) {
+ final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
+ final TaskStack splitScreenStack =
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ if (inSplitScreenPrimary || (splitScreenStack != null
+ && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) {
// The existence of a docked stack affects the size of other static stack created since
// the docked stack occupies a dedicated region on screen, but only if the dock stack is
// not fullscreen. If it's fullscreen, it means that we are in the transition of
// dismissing it, so we must not resize this stack.
bounds = new Rect();
- dc.getLogicalDisplayRect(mTmpRect);
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
mTmpRect2.setEmpty();
- if (dockedStack != null) {
- dockedStack.getRawBounds(mTmpRect2);
+ if (splitScreenStack != null) {
+ splitScreenStack.getRawBounds(mTmpRect2);
}
final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getContentWidth(),
- dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
} else if (inPinnedWindowingMode()) {
// Update the bounds based on any changes to the display info
getAnimationOrCurrentBounds(mTmpRect2);
- boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
- mTmpRect2, mTmpRect3);
- if (updated) {
+ if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3)) {
bounds = new Rect(mTmpRect3);
}
}
updateDisplayInfo(bounds);
- super.onDisplayChanged(dc);
}
/**
@@ -922,10 +927,6 @@
mAnimationBackgroundSurface = null;
}
- if (inSplitScreenPrimaryWindowingMode()) {
- mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
- }
-
mDisplayContent = null;
mService.mWindowPlacerLocked.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index df51be1..723b888 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6860,11 +6860,16 @@
public void setWillReplaceWindow(IBinder token, boolean animate) {
synchronized (mWindowMap) {
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+ if (appWindowToken == null) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+ token);
return;
}
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
+ }
appWindowToken.setWillReplaceWindows(animate);
}
}
@@ -6884,11 +6889,16 @@
void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
synchronized (mWindowMap) {
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+ if (appWindowToken == null) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+ token);
return;
}
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
+ }
if (childrenOnly) {
appWindowToken.setWillReplaceChildWindows();