Added WindowContainer.removeImmediately to support immediate removal
There are 2 types of window conatiner removals. The ones that happen
immediately and the ones that are deferred until the system is in
a good state to handle them. WindowContainer and its child classes
now support both through WC.removeImmediately() and WC.removeIfPossible()
methods.
The method names create a consistency in the codebase and also makes is
obvious what the methods will do.
Bug: 30060889
Change-Id: I459c2eef17e20cefc42e9cc542c9a3c69fc9b898
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 518b9b5..47b5f3d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -75,6 +75,7 @@
final boolean voiceInteraction;
Task mTask;
+ // TODO: Have a fillParent variable in WindowContainer to this?
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean layoutConfigChanges;
@@ -100,7 +101,7 @@
// These are to track the app's real drawing status if there were no saved surfaces.
boolean allDrawnExcludingSaved;
int numInterestingWindowsExcludingSaved;
- int numDrawnWindowsExclusingSaved;
+ int numDrawnWindowsExcludingSaved;
// Is this window's surface needed? This is almost like hidden, except
// it will sometimes be true a little earlier: when the token has
@@ -133,6 +134,7 @@
// Input application handle used by the input dispatcher.
final InputApplicationHandle mInputApplicationHandle;
+ // TODO: Have a WindowContainer state for tracking exiting/deferred removal.
boolean mIsExiting;
boolean mLaunchTaskBehind;
@@ -356,18 +358,12 @@
return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable;
}
- void removeAppFromTaskLocked() {
+ @Override
+ void removeIfPossible() {
mIsExiting = false;
removeAllWindows();
-
- // Use local variable because removeAppToken will null out mTask.
- final Task task = mTask;
- if (task != null) {
- if (!task.removeAppToken(this)) {
- Slog.e(TAG, "removeAppFromTaskLocked: token=" + this
- + " not found.");
- }
- task.mStack.mExitingAppTokens.remove(this);
+ if (mTask != null) {
+ mTask.detachChild(this);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 990405a..fb455fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -514,7 +514,7 @@
for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
AppWindowToken wtoken = tokens.get(tokenNdx);
if (wtoken.mIsExiting) {
- wtoken.removeAppFromTaskLocked();
+ wtoken.removeIfPossible();
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4241b32..754a9a6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -179,6 +179,14 @@
}
}
+ // TODO: Don't forget to switch to WC.detachChild
+ void detachChild(AppWindowToken wtoken) {
+ if (!removeAppToken(wtoken)) {
+ Slog.e(TAG, "detachChild: token=" + this + " not found.");
+ }
+ mStack.mExitingAppTokens.remove(wtoken);
+ }
+
boolean removeAppToken(AppWindowToken wtoken) {
boolean removed = mAppTokens.remove(wtoken);
if (mAppTokens.size() == 0) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fd6994c..d37d0e1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -67,17 +67,50 @@
mChildren.add(child);
}
- /** Removes this window container and its children */
+ /**
+ * Removes this window container and its children with no regard for what else might be going on
+ * in the system. For example, the container will be removed during animation if this method is
+ * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
+ * which allows the system to defer removal until a suitable time.
+ */
@CallSuper
- void remove() {
+ void removeImmediately() {
while (!mChildren.isEmpty()) {
- final WindowContainer child = mChildren.removeLast();
- child.remove();
+ final WindowContainer child = mChildren.peekLast();
+ child.removeImmediately();
+ // Need to do this after calling remove on the child because the child might try to
+ // remove/detach itself from its parent which will cause an exception if we remove
+ // it before calling remove on the child.
+ mChildren.remove(child);
}
if (mParent != null) {
- mParent.mChildren.remove(this);
- mParent = null;
+ mParent.detachChild(this);
+ }
+ }
+
+ /**
+ * Removes this window container and its children taking care not to remove them during a
+ * critical stage in the system. For example, some containers will not be removed during
+ * animation if this method is called.
+ */
+ // TODO: figure-out implementation that works best for this.
+ // E.g. when do we remove from parent list? maybe not...
+ void removeIfPossible() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mChildren.get(i);
+ wc.removeIfPossible();
+ }
+ }
+
+ /** Detaches the input child container from this container which is its parent. */
+ @CallSuper
+ void detachChild(WindowContainer child) {
+ if (mChildren.remove(child)) {
+ child.mParent = null;
+ } else {
+ throw new IllegalArgumentException("detachChild: container=" + child
+ + " is not a child of container=" + this);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 856e101..0aa1143 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -247,7 +247,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -1896,7 +1895,10 @@
/**
* Performs some centralized bookkeeping clean-up on the window that is being removed.
- * NOTE: Should only be called from {@link WindowState#remove()}
+ * NOTE: Should only be called from {@link WindowState#removeImmediately()}
+ * TODO: Maybe better handled with a method {@link WindowContainer#detachChild} if we can
+ * figure-out a good way to have all parents of a WindowState doing the same thing without
+ * forgetting to add the wiring when a new parent of WindowState is added.
*/
void postWindowRemoveCleanupLocked(WindowState win) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
@@ -3756,7 +3758,7 @@
// soon as their animations are complete
wtoken.mAppAnimator.clearAnimation();
wtoken.mAppAnimator.animating = false;
- wtoken.removeAppFromTaskLocked();
+ wtoken.removeIfPossible();
}
wtoken.removed = true;
@@ -10449,7 +10451,7 @@
public void removeWindowToken(IBinder token, boolean removeWindows) {
synchronized(mWindowMap) {
if (removeWindows) {
- WindowToken wtoken = mTokenMap.remove(token);
+ final WindowToken wtoken = mTokenMap.remove(token);
if (wtoken != null) {
wtoken.removeAllWindows();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2c359fc..4fe29cb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -187,7 +187,17 @@
boolean mEnforceSizeCompat;
int mViewVisibility;
int mSystemUiVisibility;
+ /**
+ * The visibility of the window based on policy like {@link WindowManagerPolicy}.
+ * Normally set by calling {@link #showLw} and {@link #hideLw}.
+ */
boolean mPolicyVisibility = true;
+ /**
+ * What {@link #mPolicyVisibility} should be set to after a transition animation.
+ * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and
+ * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit
+ * animation is done.
+ */
boolean mPolicyVisibilityAfterAnim = true;
boolean mAppOpVisibility = true;
boolean mAppFreezing;
@@ -1404,8 +1414,7 @@
@Override
public boolean isDrawnLw() {
return mHasSurface && !mDestroying &&
- (mWinAnimator.mDrawState == READY_TO_SHOW
- || mWinAnimator.mDrawState == HAS_DRAWN);
+ (mWinAnimator.mDrawState == READY_TO_SHOW || mWinAnimator.mDrawState == HAS_DRAWN);
}
/**
@@ -1594,12 +1603,12 @@
void onWindowReplacementTimeout() {
if (mWillReplaceWindow) {
// Since the window already timed out, remove it immediately now.
- // Use WindowState#remove() instead of WindowState#removeIfPossible(), as the latter
+ // Use WindowState#removeImmediately() instead of WindowState#removeIfPossible(), as the latter
// delays removal on certain conditions, which will leave the stale window in the
// stack and marked mWillReplaceWindow=false, so the window will never be removed.
//
// Also removes child windows.
- remove();
+ removeImmediately();
} else {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = (WindowState) mChildren.get(i);
@@ -1618,12 +1627,13 @@
}
@Override
- void remove() {
- super.remove();
+ void removeImmediately() {
+ super.removeImmediately();
if (mRemoved) {
// Nothing to do.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "WS.remove: " + this + " Already removed...");
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "WS.removeImmediately: " + this + " Already removed...");
return;
}
@@ -1660,15 +1670,13 @@
mService.postWindowRemoveCleanupLocked(this);
}
+ @Override
void removeIfPossible() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = (WindowState) mChildren.get(i);
- c.removeIfPossible(false /*keepVisibleDeadWindow*/);
- }
+ super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
- void removeIfPossible(boolean keepVisibleDeadWindow) {
+ private void removeIfPossible(boolean keepVisibleDeadWindow) {
mWindowRemovalAllowed = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG,
"removeIfPossible: " + this + " callers=" + Debug.getCallers(5));
@@ -1793,7 +1801,7 @@
}
}
- remove();
+ removeImmediately();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && mService.updateOrientationFromAppTokensLocked(false)) {
@@ -2010,7 +2018,7 @@
mReplacingRemoveRequested = false;
mReplacementWindow = null;
if (mAnimatingExit || !mAnimateReplacingWindow) {
- remove();
+ removeImmediately();
}
}
@@ -2567,7 +2575,7 @@
destroyOrSaveSurface();
}
if (mRemoveOnExit) {
- remove();
+ removeImmediately();
}
if (cleanupOnResume) {
requestUpdateWallpaperIfNeeded();
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d0c73d3..317bb35 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -215,7 +215,7 @@
while (!mService.mForceRemoves.isEmpty()) {
final WindowState ws = mService.mForceRemoves.remove(0);
Slog.i(TAG, "Force removing: " + ws);
- ws.remove();
+ ws.removeImmediately();
}
Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
Object tmp = new Object();
@@ -461,7 +461,7 @@
token.mAppAnimator.animating = false;
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"performLayout: App token exiting now removed" + token);
- token.removeAppFromTaskLocked();
+ token.removeIfPossible();
}
}
}
@@ -545,7 +545,7 @@
DisplayContentList displayList = new DisplayContentList();
for (i = 0; i < N; i++) {
final WindowState w = mService.mPendingRemoveTmp[i];
- w.remove();
+ w.removeImmediately();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
displayList.add(displayContent);
@@ -797,7 +797,7 @@
atoken.lastTransactionSequence = mService.mTransactionSequence;
atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
atoken.numInterestingWindowsExcludingSaved = 0;
- atoken.numDrawnWindowsExclusingSaved = 0;
+ atoken.numDrawnWindowsExcludingSaved = 0;
atoken.startingDisplayed = false;
}
if (!atoken.allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
@@ -840,7 +840,7 @@
if (w != atoken.startingWindow && w.isInteresting()) {
atoken.numInterestingWindowsExcludingSaved++;
if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
- atoken.numDrawnWindowsExclusingSaved++;
+ atoken.numDrawnWindowsExcludingSaved++;
if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
Slog.v(TAG, "tokenMayBeDrawnExcludingSaved: " + atoken
+ " w=" + w + " numInteresting="
@@ -1550,11 +1550,11 @@
if (!wtoken.allDrawnExcludingSaved) {
int numInteresting = wtoken.numInterestingWindowsExcludingSaved;
if (numInteresting > 0
- && wtoken.numDrawnWindowsExclusingSaved >= numInteresting) {
+ && wtoken.numDrawnWindowsExcludingSaved >= numInteresting) {
if (DEBUG_VISIBILITY)
Slog.v(TAG, "allDrawnExcludingSaved: " + wtoken
+ " interesting=" + numInteresting
- + " drawn=" + wtoken.numDrawnWindowsExclusingSaved);
+ + " drawn=" + wtoken.numDrawnWindowsExcludingSaved);
wtoken.allDrawnExcludingSaved = true;
displayContent.layoutNeeded = true;
if (wtoken.isAnimatingInvisibleWithSavedSurface()
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index ea8b7bb..24d797b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -98,7 +98,7 @@
assertFalse(child2.hasChild(child12));
}
- public void testRemove() throws Exception {
+ public void testRemoveImmediately() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
final TestWindowContainer root = builder.setLayer(0).build();
@@ -108,13 +108,16 @@
final TestWindowContainer child12 = child1.addChildWindow();
final TestWindowContainer child21 = child2.addChildWindow();
- child12.remove();
+ assertNotNull(child12.getParentWindow());
+ child12.removeImmediately();
assertNull(child12.getParentWindow());
assertEquals(1, child1.getChildrenCount());
assertFalse(child1.hasChild(child12));
assertFalse(root.hasChild(child12));
- child2.remove();
+ assertTrue(root.hasChild(child2));
+ assertNotNull(child2.getParentWindow());
+ child2.removeImmediately();
assertNull(child2.getParentWindow());
assertNull(child21.getParentWindow());
assertEquals(0, child2.getChildrenCount());
@@ -125,7 +128,7 @@
assertTrue(root.hasChild(child1));
assertTrue(root.hasChild(child11));
- root.remove();
+ root.removeImmediately();
assertEquals(0, root.getChildrenCount());
}
@@ -183,6 +186,32 @@
assertFalse(child21.isVisible());
}
+ public void testDetachChild() throws Exception {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer root = builder.setLayer(0).build();
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+ final TestWindowContainer child11 = child1.addChildWindow();
+ final TestWindowContainer child21 = child2.addChildWindow();
+
+ assertTrue(root.hasChild(child2));
+ assertTrue(root.hasChild(child21));
+ root.detachChild(child2);
+ assertFalse(root.hasChild(child2));
+ assertFalse(root.hasChild(child21));
+ assertNull(child2.getParentWindow());
+
+ boolean gotException = false;
+ assertTrue(root.hasChild(child11));
+ try {
+ // Can only detach our direct children.
+ root.detachChild(child11);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private class TestWindowContainer extends WindowContainer {
private final int mLayer;
@@ -190,6 +219,8 @@
private final boolean mCanDetach;
private boolean mIsAnimating;
private boolean mIsVisible;
+ private int mRemoveIfPossibleCount;
+ private int mRemoveImmediatelyCount;
/**
* Compares 2 window layers and returns -1 if the first is lesser than the second in terms
@@ -252,6 +283,18 @@
boolean isVisible() {
return mIsVisible || super.isVisible();
}
+
+ @Override
+ void removeImmediately() {
+ super.removeImmediately();
+ mRemoveImmediatelyCount++;
+ }
+
+ @Override
+ void removeIfPossible() {
+ super.removeIfPossible();
+ mRemoveIfPossibleCount++;
+ }
}
private class TestWindowContainerBuilder {