diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 4bede4c3623f6..b4bc0f5b3a32d 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -813,7 +813,7 @@ class ActivityStack extends Task { /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */, - true /* moveParentsToTop */); + true /* moveDisplayToTop */); mRootWindowContainer.resumeFocusedStacksTopActivities(); // Update visibility of activities before notifying WM. This way it won't try to resize // windows that are no longer visible. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 56ad9643e619e..0ead575051a1e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2638,7 +2638,7 @@ class Task extends WindowContainer { */ ActivityStack adjustFocusToNextFocusableTask(String reason) { return adjustFocusToNextFocusableTask(reason, false /* allowFocusSelf */, - true /* moveParentsToTop */); + true /* moveDisplayToTop */); } /** Return the next focusable task by looking from the siblings and parent tasks */ @@ -2661,11 +2661,11 @@ class Task extends WindowContainer { * Find next proper focusable task and make it focused. * @param reason The reason of making the adjustment. * @param allowFocusSelf Is the focus allowed to remain on the same task. - * @param moveParentsToTop Whether to move parents to top while making the task focused. + * @param moveDisplayToTop Whether to move display to top while making the task focused. * @return The root task that now got the focus, {@code null} if none found. */ ActivityStack adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf, - boolean moveParentsToTop) { + boolean moveDisplayToTop) { ActivityStack focusableTask = (ActivityStack) getNextFocusableTask(allowFocusSelf); if (focusableTask == null) { focusableTask = mRootWindowContainer.getNextFocusableStack((ActivityStack) this, @@ -2676,10 +2676,17 @@ class Task extends WindowContainer { } final ActivityStack rootTask = (ActivityStack) focusableTask.getRootTask(); - if (!moveParentsToTop) { - // Only move the next stack to top in its task container. + if (!moveDisplayToTop) { + // There may be multiple task layers above this task, so when relocating the task to the + // top, we should move this task and each of its parent task that below display area to + // the top of each layer. WindowContainer parent = focusableTask.getParent(); - parent.positionChildAt(POSITION_TOP, focusableTask, false /* includingParents */); + WindowContainer next = focusableTask; + do { + parent.positionChildAt(POSITION_TOP, next, false /* includingParents */); + next = parent; + parent = next.getParent(); + } while (next.asTask() != null && parent != null); return rootTask; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 102c2a6364f4e..fbb2b7b3dccab 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -299,8 +299,17 @@ final class TaskDisplayArea extends DisplayArea { @Override void positionChildAt(int position, ActivityStack child, boolean includingParents) { - final boolean moveToTop = (position == POSITION_TOP || position == getChildCount()); + final boolean moveToTop = position >= getChildCount() - 1; final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0); + + // Reset mPreferredTopFocusableStack before positioning to top or {@link + // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top + // resumed activity. + final boolean wasContained = mChildren.contains(child); + if (moveToTop && wasContained && child.isFocusable()) { + mPreferredTopFocusableStack = null; + } + if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) { // This stack is always-on-top, override the default behavior. Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom"); @@ -330,6 +339,17 @@ final class TaskDisplayArea extends DisplayArea { child.updateTaskMovement(moveToTop); mDisplayContent.setLayoutNeeded(); + + // The insert position may be adjusted to non-top when there is always-on-top stack. Since + // the original position is preferred to be top, the stack should have higher priority when + // we are looking for top focusable stack. The condition {@code wasContained} restricts the + // preferred stack is set only when moving an existing stack to top instead of adding a new + // stack that may be too early (e.g. in the middle of launching or reparenting). + if (moveToTop && child.isFocusableAndVisible()) { + mPreferredTopFocusableStack = child; + } else if (mPreferredTopFocusableStack == child) { + mPreferredTopFocusableStack = null; + } } /** @@ -727,29 +747,10 @@ final class TaskDisplayArea extends DisplayArea { "positionStackAt: Can only have one task on display=" + this); } - final boolean movingToTop = wasContained && position >= getStackCount() - 1; - // Reset mPreferredTopFocusableStack before positioning to top or {@link - // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top - // resumed activity. - if (movingToTop && stack.isFocusable()) { - mPreferredTopFocusableStack = null; - } - // Since positionChildAt() is called during the creation process of pinned stacks, // ActivityStack#getStack() can be null. positionStackAt(position, stack, includingParents); - // The insert position may be adjusted to non-top when there is always-on-top stack. Since - // the original position is preferred to be top, the stack should have higher priority when - // we are looking for top focusable stack. The condition {@code wasContained} restricts the - // preferred stack is set only when moving an existing stack to top instead of adding a new - // stack that may be too early (e.g. in the middle of launching or reparenting). - if (movingToTop && stack.isFocusableAndVisible()) { - mPreferredTopFocusableStack = stack; - } else if (mPreferredTopFocusableStack == stack) { - mPreferredTopFocusableStack = null; - } - if (updateLastFocusedStackReason != null) { final ActivityStack currentFocusedStack = getFocusedStack(); if (currentFocusedStack != prevFocusedStack) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 4ee933a0a5a55..29b96ebdc0904 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -793,6 +793,39 @@ public class ActivityRecordTests extends ActivityTestsBase { assertEquals(mTask, mStack.getTopMostTask()); } + /** + * Verify that when top focused activity is on secondary display, when finishing the top focused + * activity on default display, the preferred top stack on default display should be changed by + * adjusting focus. + */ + @Test + public void testFinishActivityIfPossible_PreferredTopStackChanged() { + final ActivityRecord topActivityOnNonTopDisplay = + createActivityOnDisplay(true /* defaultDisplay */, null /* process */); + ActivityStack topRootableTask = topActivityOnNonTopDisplay.getRootTask(); + topRootableTask.moveToFront("test"); + assertTrue(topRootableTask.isTopStackInDisplayArea()); + assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea() + .mPreferredTopFocusableStack); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + topRootableTask = secondaryDisplayActivity.getRootTask(); + topRootableTask.moveToFront("test"); + assertTrue(topRootableTask.isTopStackInDisplayArea()); + assertEquals(topRootableTask, + secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableStack); + + // The global top focus activity is on secondary display now. + // Finish top activity on default display and verify the next preferred top focusable stack + // on default display has changed. + topActivityOnNonTopDisplay.setState(RESUMED, "test"); + topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */, + null /* resultGrants */, "test", false /* oomAdj */); + assertEquals(mTask, mStack.getTopMostTask()); + assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack); + } + /** * Verify that resumed activity is paused due to finish request. */