diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5a6e0a1ff9c30267646092d99d6a43c7aa57257c..982785e6339e4ac519dbebb06b1d10bdc58cebcf 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3366,8 +3366,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing starting %s from %s", tStartingWindow, fromActivity); fromActivity.removeChild(tStartingWindow); - fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); - fromActivity.mVisibleSetFromTransferredStartingWindow = false; addWindow(tStartingWindow); // Propagate other interesting state between the tokens. If the old token is displayed, @@ -3394,6 +3392,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // we've transferred the animation. mUseTransferredAnimation = true; } + // Post cleanup after the visibility and animation are transferred. + fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); + fromActivity.mVisibleSetFromTransferredStartingWindow = false; mWmService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 9b9b613403321ca05fe61f8982103e423695b208..197941aa92b96525f7b45f5e55a77431a87e71b8 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2140,7 +2140,7 @@ class ActivityStack extends Task { // window manager to keep the previous window it had previously // created, if it still had one. Task prevTask = r.getTask(); - ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked(); + ActivityRecord prev = prevTask.topActivityWithStartingWindow(); if (prev != null) { // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index c28d47cdbe801f707031295eeb56bfa30d3284fb..c4ccd00d9b0a35cf62afd2b690f06cffd8a41958 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -467,28 +467,33 @@ public class ActivityStartController { final ActivityRecord[] outActivity = new ActivityRecord[1]; // Lock the loop to ensure the activities launched in a sequence. synchronized (mService.mGlobalLock) { - for (int i = 0; i < starters.length; i++) { - final int startResult = starters[i].setResultTo(resultTo) - .setOutActivity(outActivity).execute(); - if (startResult < START_SUCCESS) { - // Abort by error result and recycle unused starters. - for (int j = i + 1; j < starters.length; j++) { - mFactory.recycle(starters[j]); + mService.deferWindowLayout(); + try { + for (int i = 0; i < starters.length; i++) { + final int startResult = starters[i].setResultTo(resultTo) + .setOutActivity(outActivity).execute(); + if (startResult < START_SUCCESS) { + // Abort by error result and recycle unused starters. + for (int j = i + 1; j < starters.length; j++) { + mFactory.recycle(starters[j]); + } + return startResult; } - return startResult; - } - final ActivityRecord started = outActivity[0]; - if (started != null && started.getUid() == filterCallingUid) { - // Only the started activity which has the same uid as the source caller can - // be the caller of next activity. - resultTo = started.appToken; - } else { - resultTo = sourceResultTo; - // Different apps not adjacent to the caller are forced to be new task. - if (i < starters.length - 1) { - starters[i + 1].getIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final ActivityRecord started = outActivity[0]; + if (started != null && started.getUid() == filterCallingUid) { + // Only the started activity which has the same uid as the source caller + // can be the caller of next activity. + resultTo = started.appToken; + } else { + resultTo = sourceResultTo; + // Different apps not adjacent to the caller are forced to be new task. + if (i < starters.length - 1) { + starters[i + 1].getIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } } } + } finally { + mService.continueWindowLayout(); } } } finally { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f8ad6f248728fcae1185a5332e8ac30e7980f802..db86ea636c103adc2e445ead41b44e4a71aa688e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1309,12 +1309,12 @@ class Task extends WindowContainer { return isUidPresent; } - ActivityRecord topRunningActivityWithStartingWindowLocked() { + ActivityRecord topActivityWithStartingWindow() { if (getParent() == null) { return null; } return getActivity((r) -> r.mStartingWindowState == STARTING_WINDOW_SHOWN - && r.canBeTopRunning()); + && r.okToShowLocked()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 07050d9666d5bda3388d20e8c8efd10fbd4e9f08..36d4888fa56e8a0a9b1609883e78293e68201ab8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; @@ -47,6 +48,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -390,6 +392,46 @@ public class AppWindowTokenTests extends WindowTestsBase { onAnimationFinishedCallback)); } + @Test + public void testTransferStartingWindowFromFinishingActivity() { + mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, + "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */, + null /* transferFrom */, true /* newTask */, true /* taskSwitch */, + false /* processRunning */, false /* allowTaskSnapshot */, + false /* activityCreate */); + waitUntilHandlersIdle(); + assertHasStartingWindow(mActivity); + mActivity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN; + + doCallRealMethod().when(mStack).startActivityLocked( + any(), any(), anyBoolean(), anyBoolean(), any()); + // Make mVisibleSetFromTransferredStartingWindow true. + final ActivityRecord middle = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setTask(mTask).build(); + mStack.startActivityLocked(middle, null /* focusedTopActivity */, + false /* newTask */, false /* keepCurTransition */, null /* options */); + middle.makeFinishingLocked(); + + assertNull(mActivity.startingWindow); + assertHasStartingWindow(middle); + + final ActivityRecord top = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setTask(mTask).build(); + // Expect the visibility should be updated to true when transferring starting window from + // a visible activity. + top.setVisible(false); + // The finishing middle should be able to transfer starting window to top. + mStack.startActivityLocked(top, null /* focusedTopActivity */, + false /* newTask */, false /* keepCurTransition */, null /* options */); + + assertNull(middle.startingWindow); + assertHasStartingWindow(top); + assertTrue(top.isVisible()); + // The activity was visible by mVisibleSetFromTransferredStartingWindow, so after its + // starting window is transferred, it should restore to invisible. + assertFalse(middle.isVisible()); + } + private ActivityRecord createIsolatedTestActivityRecord() { final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(taskStack, 0 /* userId */);