Loading services/core/java/com/android/server/wm/ActivityDisplay.java +36 −14 Original line number Diff line number Diff line Loading @@ -1031,29 +1031,50 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> void remove() { final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove(); ActivityStack lastReparentedStack = null; mPreferredTopFocusableStack = null; // Stacks could be reparented from the removed display to other display. While // reparenting the last stack of the removed display, the remove display is ready to be // released (no more ActivityStack). But, we cannot release it at that moment or the // related WindowContainer and WindowContainerController will also be removed. So, we // set display as removed after reparenting stack finished. for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay(); mSupervisor.beginDeferResume(); try { int numStacks = mStacks.size(); // Keep the order from bottom to top. for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { final ActivityStack stack = mStacks.get(stackNdx); // Always finish non-standard type stacks. if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) { stack.finishAllActivitiesLocked(true /* immediately */); } else { // If default display is in split-window mode, set windowing mode of the stack to // split-screen secondary. Otherwise, set the windowing mode to undefined by // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by // default to let stack inherited the windowing mode from the new display. int windowingMode = mSupervisor.getDefaultDisplay().hasSplitScreenPrimaryStack() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; mSupervisor.moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY, true); final int windowingMode = toDisplay.hasSplitScreenPrimaryStack() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; stack.reparent(toDisplay, true /* onTop */, true /* displayRemoved */); stack.setWindowingMode(windowingMode); lastReparentedStack = stack; } // Stacks may be removed from this display. Ensure each stack will be processed and // the loop will end. stackNdx -= numStacks - mStacks.size(); numStacks = mStacks.size(); } } finally { mSupervisor.endDeferResume(); } mRemoved = true; // Only update focus/visibility for the last one because there may be many stacks are // reparented and the intermediate states are unnecessary. if (lastReparentedStack != null) { lastReparentedStack.postReparent(); } releaseSelfIfNeeded(); if (!mAllSleepTokens.isEmpty()) { Loading Loading @@ -1091,7 +1112,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> || mDisplayId == mSupervisor.mService.mVr2dDisplayId; } private boolean shouldDestroyContentOnRemove() { @VisibleForTesting boolean shouldDestroyContentOnRemove() { return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT; } Loading services/core/java/com/android/server/wm/ActivityStack.java +10 −2 Original line number Diff line number Diff line Loading @@ -738,7 +738,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** Adds the stack to specified display and calls WindowManager to do the same. */ void reparent(ActivityDisplay activityDisplay, boolean onTop) { void reparent(ActivityDisplay activityDisplay, boolean onTop, boolean displayRemoved) { // TODO: We should probably resolve the windowing mode for the stack on the new display here // so that it end up in a compatible mode in the new display. e.g. split-screen secondary. removeFromDisplay(); Loading @@ -747,6 +747,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mTmpRect2.setEmpty(); mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); if (!displayRemoved) { postReparent(); } } /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); // Update visibility of activities before notifying WM. This way it won't try to resize Loading Loading @@ -1162,7 +1169,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean isAttached() { return getParent() != null; final ActivityDisplay display = getDisplay(); return display != null && !display.isRemoved(); } /** Loading services/core/java/com/android/server/wm/ActivityStackSupervisor.java +3 −3 Original line number Diff line number Diff line Loading @@ -3173,7 +3173,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D + " to its current displayId=" + displayId); } stack.reparent(activityDisplay, onTop); stack.reparent(activityDisplay, onTop, false /* displayRemoved */); // TODO(multi-display): resize stacks properly if moved from split-screen. } Loading Loading @@ -4568,14 +4568,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** * Begin deferring resume to avoid duplicate resumes in one pass. */ private void beginDeferResume() { void beginDeferResume() { mDeferResumeCount++; } /** * End deferring resume and determine if resume can be called. */ private void endDeferResume() { void endDeferResume() { mDeferResumeCount--; } Loading services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +39 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; Loading @@ -27,7 +28,12 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.platform.test.annotations.Presubmit; Loading Loading @@ -120,6 +126,39 @@ public class ActivityDisplayTests extends ActivityTestsBase { assertTrue(stack2.isFocusedStackOnDisplay()); } /** * Verifies {@link ActivityDisplay#remove} should not resume home stack on the removing display. */ @Test public void testNotResumeHomeStackOnRemovingDisplay() { // Create a display which supports system decoration and allows reparenting stacks to // another display when the display is removed. final ActivityDisplay display = spy(createNewActivityDisplay()); doReturn(false).when(display).shouldDestroyContentOnRemove(); doReturn(true).when(display).supportsSystemDecorations(); mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP); // Put home stack on the display. final ActivityStack homeStack = display.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); final TaskRecord task = new TaskBuilder(mSupervisor).setStack(homeStack).build(); new ActivityBuilder(mService).setTask(task).build(); display.removeChild(homeStack); final ActivityStack spiedHomeStack = spy(homeStack); display.addChild(spiedHomeStack, ActivityDisplay.POSITION_TOP); reset(spiedHomeStack); // Put a finishing standard activity which will be reparented. final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display); stack.topRunningActivityLocked().makeFinishingLocked(); display.remove(); // The removed display should have no focused stack and its home stack should never resume. assertNull(display.getFocusedStack()); verify(spiedHomeStack, never()).resumeTopActivityUncheckedLocked(any(), any()); } private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) { final ActivityStack fullscreenStack = display.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); Loading Loading
services/core/java/com/android/server/wm/ActivityDisplay.java +36 −14 Original line number Diff line number Diff line Loading @@ -1031,29 +1031,50 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> void remove() { final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove(); ActivityStack lastReparentedStack = null; mPreferredTopFocusableStack = null; // Stacks could be reparented from the removed display to other display. While // reparenting the last stack of the removed display, the remove display is ready to be // released (no more ActivityStack). But, we cannot release it at that moment or the // related WindowContainer and WindowContainerController will also be removed. So, we // set display as removed after reparenting stack finished. for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay(); mSupervisor.beginDeferResume(); try { int numStacks = mStacks.size(); // Keep the order from bottom to top. for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { final ActivityStack stack = mStacks.get(stackNdx); // Always finish non-standard type stacks. if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) { stack.finishAllActivitiesLocked(true /* immediately */); } else { // If default display is in split-window mode, set windowing mode of the stack to // split-screen secondary. Otherwise, set the windowing mode to undefined by // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by // default to let stack inherited the windowing mode from the new display. int windowingMode = mSupervisor.getDefaultDisplay().hasSplitScreenPrimaryStack() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; mSupervisor.moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY, true); final int windowingMode = toDisplay.hasSplitScreenPrimaryStack() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; stack.reparent(toDisplay, true /* onTop */, true /* displayRemoved */); stack.setWindowingMode(windowingMode); lastReparentedStack = stack; } // Stacks may be removed from this display. Ensure each stack will be processed and // the loop will end. stackNdx -= numStacks - mStacks.size(); numStacks = mStacks.size(); } } finally { mSupervisor.endDeferResume(); } mRemoved = true; // Only update focus/visibility for the last one because there may be many stacks are // reparented and the intermediate states are unnecessary. if (lastReparentedStack != null) { lastReparentedStack.postReparent(); } releaseSelfIfNeeded(); if (!mAllSleepTokens.isEmpty()) { Loading Loading @@ -1091,7 +1112,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> || mDisplayId == mSupervisor.mService.mVr2dDisplayId; } private boolean shouldDestroyContentOnRemove() { @VisibleForTesting boolean shouldDestroyContentOnRemove() { return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT; } Loading
services/core/java/com/android/server/wm/ActivityStack.java +10 −2 Original line number Diff line number Diff line Loading @@ -738,7 +738,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** Adds the stack to specified display and calls WindowManager to do the same. */ void reparent(ActivityDisplay activityDisplay, boolean onTop) { void reparent(ActivityDisplay activityDisplay, boolean onTop, boolean displayRemoved) { // TODO: We should probably resolve the windowing mode for the stack on the new display here // so that it end up in a compatible mode in the new display. e.g. split-screen secondary. removeFromDisplay(); Loading @@ -747,6 +747,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mTmpRect2.setEmpty(); mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); if (!displayRemoved) { postReparent(); } } /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); // Update visibility of activities before notifying WM. This way it won't try to resize Loading Loading @@ -1162,7 +1169,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean isAttached() { return getParent() != null; final ActivityDisplay display = getDisplay(); return display != null && !display.isRemoved(); } /** Loading
services/core/java/com/android/server/wm/ActivityStackSupervisor.java +3 −3 Original line number Diff line number Diff line Loading @@ -3173,7 +3173,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D + " to its current displayId=" + displayId); } stack.reparent(activityDisplay, onTop); stack.reparent(activityDisplay, onTop, false /* displayRemoved */); // TODO(multi-display): resize stacks properly if moved from split-screen. } Loading Loading @@ -4568,14 +4568,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** * Begin deferring resume to avoid duplicate resumes in one pass. */ private void beginDeferResume() { void beginDeferResume() { mDeferResumeCount++; } /** * End deferring resume and determine if resume can be called. */ private void endDeferResume() { void endDeferResume() { mDeferResumeCount--; } Loading
services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +39 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; Loading @@ -27,7 +28,12 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.platform.test.annotations.Presubmit; Loading Loading @@ -120,6 +126,39 @@ public class ActivityDisplayTests extends ActivityTestsBase { assertTrue(stack2.isFocusedStackOnDisplay()); } /** * Verifies {@link ActivityDisplay#remove} should not resume home stack on the removing display. */ @Test public void testNotResumeHomeStackOnRemovingDisplay() { // Create a display which supports system decoration and allows reparenting stacks to // another display when the display is removed. final ActivityDisplay display = spy(createNewActivityDisplay()); doReturn(false).when(display).shouldDestroyContentOnRemove(); doReturn(true).when(display).supportsSystemDecorations(); mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP); // Put home stack on the display. final ActivityStack homeStack = display.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); final TaskRecord task = new TaskBuilder(mSupervisor).setStack(homeStack).build(); new ActivityBuilder(mService).setTask(task).build(); display.removeChild(homeStack); final ActivityStack spiedHomeStack = spy(homeStack); display.addChild(spiedHomeStack, ActivityDisplay.POSITION_TOP); reset(spiedHomeStack); // Put a finishing standard activity which will be reparented. final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display); stack.topRunningActivityLocked().makeFinishingLocked(); display.remove(); // The removed display should have no focused stack and its home stack should never resume. assertNull(display.getFocusedStack()); verify(spiedHomeStack, never()).resumeTopActivityUncheckedLocked(any(), any()); } private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) { final ActivityStack fullscreenStack = display.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); Loading