Loading services/core/java/com/android/server/wm/ActivityRecord.java +12 −4 Original line number Diff line number Diff line Loading @@ -1699,6 +1699,12 @@ final class ActivityRecord extends ConfigurationContainer { return FINISH_RESULT_CANCELLED; } final ActivityStack stack = getActivityStack(); final boolean mayAdjustFocus = (isState(RESUMED) || stack.mResumedActivity == null) // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. && mRootActivityContainer.isTopDisplayFocusedStack(stack); mAtmService.mWindowManager.deferSurfaceLayout(); try { makeFinishingLocked(); Loading @@ -1720,8 +1726,12 @@ final class ActivityRecord extends ConfigurationContainer { pauseKeyDispatchingLocked(); final ActivityStack stack = getActivityStack(); stack.adjustFocusedActivityStack(this, "finishIfPossible"); // We are finishing the top focused activity and its stack has nothing to be focused so // the next focusable stack should be focused. if (mayAdjustFocus && (stack.topRunningActivityLocked() == null || !stack.isFocusable())) { stack.adjustFocusToNextFocusableStack("finish-top"); } finishActivityResults(resultCode, resultData); Loading Loading @@ -3012,7 +3022,6 @@ final class ActivityRecord extends ConfigurationContainer { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this); if (finishIfPossible("stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) { // {@link adjustFocusedActivityStack} must have been already called. resumeKeyDispatchingLocked(); return; } Loading @@ -3028,7 +3037,6 @@ final class ActivityRecord extends ConfigurationContainer { if (!attachedToProcess()) { return; } stack.adjustFocusedActivityStack(this, "stopActivity"); resumeKeyDispatchingLocked(); try { stopped = false; Loading services/core/java/com/android/server/wm/ActivityStack.java +4 −44 Original line number Diff line number Diff line Loading @@ -3542,50 +3542,6 @@ class ActivityStack extends ConfigurationContainer { return taskTop; } void adjustFocusedActivityStack(ActivityRecord r, String reason) { if (!mRootActivityContainer.isTopDisplayFocusedStack(this) || ((mResumedActivity != r) && (mResumedActivity != null))) { return; } final ActivityRecord next = topRunningActivityLocked(); final String myReason = reason + " adjustFocus"; if (next == r) { final ActivityRecord top = mRootActivityContainer.topRunningActivity(); if (top != null) { top.moveFocusableActivityToTop(myReason); } return; } if (next != null && isFocusable()) { // Keep focus in stack if we have a top running activity and are focusable. return; } // Task is not guaranteed to be non-null. For example, destroying the // {@link ActivityRecord} will disassociate the task from the activity. final TaskRecord task = r.getTaskRecord(); if (task == null) { throw new IllegalStateException("activity no longer associated with task:" + r); } // Move focus to next focusable stack if possible. final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason); if (nextFocusableStack != null) { final ActivityRecord top = nextFocusableStack.topRunningActivityLocked(); if (top != null && top == mRootActivityContainer.getTopResumedActivity()) { mService.setResumedActivityUncheckLocked(top, reason); } return; } // Whatever...go home. getDisplay().moveHomeActivityToTop(myReason); } /** * Find next proper focusable stack and make it focused. * @return The stack that now got the focus, {@code null} if none found. Loading Loading @@ -3620,6 +3576,10 @@ class ActivityStack extends ConfigurationContainer { // Top display focused stack is changed, update top resumed activity if needed. if (stack.mResumedActivity != null) { mStackSupervisor.updateTopResumedActivityIfNeeded(); // Set focused app directly because if the next focused activity is already resumed // (e.g. the next top activity is on a different display), there won't have activity // state change to update it. mService.setResumedActivityUncheckLocked(stack.mResumedActivity, reason); } return stack; } Loading services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -799,6 +799,32 @@ public class ActivityRecordTests extends ActivityTestsBase { assertFalse(mActivity.isInStackLocked()); } /** * Verify that when finishing the top focused activity on top display, the stack order will be * changed by adjusting focus. */ @Test public void testFinishActivityIfPossible_adjustStackOrder() { // Prepare the stacks with order (top to bottom): mStack, stack1, stack2. final ActivityStack stack1 = new StackBuilder(mRootActivityContainer).build(); mStack.moveToFront("test"); // The stack2 is needed here for moving back to simulate the // {@link ActivityDisplay#mPreferredTopFocusableStack} is cleared, so // {@link ActivityDisplay#getFocusedStack} will rely on the order of focusable-and-visible // stacks. Then when mActivity is finishing, its stack will be invisible (no running // activities in the stack) that is the key condition to verify. final ActivityStack stack2 = new StackBuilder(mRootActivityContainer).build(); stack2.moveToBack("test", stack2.getChildAt(0)); assertTrue(mStack.isTopStackOnDisplay()); mActivity.setState(RESUMED, "test"); mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, "test", false /* oomAdj */, false /* pauseImmediately */); assertTrue(stack1.isTopStackOnDisplay()); } /** * Verify that resumed activity is paused due to finish request. */ Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +12 −4 Original line number Diff line number Diff line Loading @@ -1699,6 +1699,12 @@ final class ActivityRecord extends ConfigurationContainer { return FINISH_RESULT_CANCELLED; } final ActivityStack stack = getActivityStack(); final boolean mayAdjustFocus = (isState(RESUMED) || stack.mResumedActivity == null) // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. && mRootActivityContainer.isTopDisplayFocusedStack(stack); mAtmService.mWindowManager.deferSurfaceLayout(); try { makeFinishingLocked(); Loading @@ -1720,8 +1726,12 @@ final class ActivityRecord extends ConfigurationContainer { pauseKeyDispatchingLocked(); final ActivityStack stack = getActivityStack(); stack.adjustFocusedActivityStack(this, "finishIfPossible"); // We are finishing the top focused activity and its stack has nothing to be focused so // the next focusable stack should be focused. if (mayAdjustFocus && (stack.topRunningActivityLocked() == null || !stack.isFocusable())) { stack.adjustFocusToNextFocusableStack("finish-top"); } finishActivityResults(resultCode, resultData); Loading Loading @@ -3012,7 +3022,6 @@ final class ActivityRecord extends ConfigurationContainer { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this); if (finishIfPossible("stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) { // {@link adjustFocusedActivityStack} must have been already called. resumeKeyDispatchingLocked(); return; } Loading @@ -3028,7 +3037,6 @@ final class ActivityRecord extends ConfigurationContainer { if (!attachedToProcess()) { return; } stack.adjustFocusedActivityStack(this, "stopActivity"); resumeKeyDispatchingLocked(); try { stopped = false; Loading
services/core/java/com/android/server/wm/ActivityStack.java +4 −44 Original line number Diff line number Diff line Loading @@ -3542,50 +3542,6 @@ class ActivityStack extends ConfigurationContainer { return taskTop; } void adjustFocusedActivityStack(ActivityRecord r, String reason) { if (!mRootActivityContainer.isTopDisplayFocusedStack(this) || ((mResumedActivity != r) && (mResumedActivity != null))) { return; } final ActivityRecord next = topRunningActivityLocked(); final String myReason = reason + " adjustFocus"; if (next == r) { final ActivityRecord top = mRootActivityContainer.topRunningActivity(); if (top != null) { top.moveFocusableActivityToTop(myReason); } return; } if (next != null && isFocusable()) { // Keep focus in stack if we have a top running activity and are focusable. return; } // Task is not guaranteed to be non-null. For example, destroying the // {@link ActivityRecord} will disassociate the task from the activity. final TaskRecord task = r.getTaskRecord(); if (task == null) { throw new IllegalStateException("activity no longer associated with task:" + r); } // Move focus to next focusable stack if possible. final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason); if (nextFocusableStack != null) { final ActivityRecord top = nextFocusableStack.topRunningActivityLocked(); if (top != null && top == mRootActivityContainer.getTopResumedActivity()) { mService.setResumedActivityUncheckLocked(top, reason); } return; } // Whatever...go home. getDisplay().moveHomeActivityToTop(myReason); } /** * Find next proper focusable stack and make it focused. * @return The stack that now got the focus, {@code null} if none found. Loading Loading @@ -3620,6 +3576,10 @@ class ActivityStack extends ConfigurationContainer { // Top display focused stack is changed, update top resumed activity if needed. if (stack.mResumedActivity != null) { mStackSupervisor.updateTopResumedActivityIfNeeded(); // Set focused app directly because if the next focused activity is already resumed // (e.g. the next top activity is on a different display), there won't have activity // state change to update it. mService.setResumedActivityUncheckLocked(stack.mResumedActivity, reason); } return stack; } Loading
services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -799,6 +799,32 @@ public class ActivityRecordTests extends ActivityTestsBase { assertFalse(mActivity.isInStackLocked()); } /** * Verify that when finishing the top focused activity on top display, the stack order will be * changed by adjusting focus. */ @Test public void testFinishActivityIfPossible_adjustStackOrder() { // Prepare the stacks with order (top to bottom): mStack, stack1, stack2. final ActivityStack stack1 = new StackBuilder(mRootActivityContainer).build(); mStack.moveToFront("test"); // The stack2 is needed here for moving back to simulate the // {@link ActivityDisplay#mPreferredTopFocusableStack} is cleared, so // {@link ActivityDisplay#getFocusedStack} will rely on the order of focusable-and-visible // stacks. Then when mActivity is finishing, its stack will be invisible (no running // activities in the stack) that is the key condition to verify. final ActivityStack stack2 = new StackBuilder(mRootActivityContainer).build(); stack2.moveToBack("test", stack2.getChildAt(0)); assertTrue(mStack.isTopStackOnDisplay()); mActivity.setState(RESUMED, "test"); mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, "test", false /* oomAdj */, false /* pauseImmediately */); assertTrue(stack1.isTopStackOnDisplay()); } /** * Verify that resumed activity is paused due to finish request. */ Loading