Merge "Fix unable to cleanup a removed display"

This commit is contained in:
TreeHugger Robot
2018-11-08 05:29:23 +00:00
committed by Android (Google) Code Review
4 changed files with 88 additions and 19 deletions

View File

@@ -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);
// 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
// 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);
stack.setWindowingMode(windowingMode);
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
// default to let stack inherited the windowing mode from the new display.
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()) {
@@ -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;
}

View File

@@ -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();
@@ -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
@@ -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();
}
/**

View File

@@ -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.
}
@@ -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--;
}

View File

@@ -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;
@@ -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;
@@ -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);