Merge "Avoid detached surface from returning to hierarchy" into main
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0b36c7e..31f4d08 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -880,8 +880,6 @@
})
@interface SplashScreenBehavior { }
- // TODO: Have a WindowContainer state for tracking exiting/deferred removal.
- boolean mIsExiting;
// Force an app transition to be ran in the case the visibility of the app did not change.
// We use this for the case of moving a Root Task to the back with multiple activities, and the
// top activity enters PIP; the bottom activity's visibility stays the same, but we need to
@@ -1227,10 +1225,9 @@
pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn);
pw.println(")");
}
- if (mStartingData != null || firstWindowDrawn || mIsExiting) {
+ if (mStartingData != null || firstWindowDrawn) {
pw.print(prefix); pw.print("startingData="); pw.print(mStartingData);
- pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
- pw.print(" mIsExiting="); pw.println(mIsExiting);
+ pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn);
}
if (mStartingWindow != null || mStartingData != null || mStartingSurface != null
|| startingMoved || mVisibleSetFromTransferredStartingWindow) {
@@ -4370,21 +4367,6 @@
super.removeImmediately();
}
- @Override
- void removeIfPossible() {
- mIsExiting = false;
- removeAllWindowsIfPossible();
- removeImmediately();
- }
-
- @Override
- boolean handleCompleteDeferredRemoval() {
- if (mIsExiting) {
- removeIfPossible();
- }
- return super.handleCompleteDeferredRemoval();
- }
-
void onRemovedFromDisplay() {
if (mRemovingFromDisplay) {
return;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 188b368..1659f7b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1753,7 +1753,6 @@
}
}
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
static boolean containsChangeFor(WindowContainer wc, ArrayList<ChangeInfo> list) {
for (int i = list.size() - 1; i >= 0; --i) {
if (list.get(i).mContainer == wc) return true;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index b7fe327..87bdfa4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -471,6 +471,16 @@
return false;
}
+ /** Returns {@code true} if the `wc` is a target of a playing transition. */
+ boolean isPlayingTarget(@NonNull WindowContainer<?> wc) {
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (Transition.containsChangeFor(wc, mPlayingTransitions.get(i).mTargets)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/** Returns {@code true} if the finishing transition contains `wc`. */
boolean inFinishingTransition(WindowContainer<?> wc) {
return mFinishingTransition != null && mFinishingTransition.isInTransition(wc);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ebf645d..f4ad030 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2109,7 +2109,7 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token);
// Window will already be removed from token before this post clean-up method is called.
if (token.isEmpty() && !token.mPersistOnEmpty) {
- token.removeImmediately();
+ token.removeIfPossible();
}
if (win.mActivityRecord != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7e7ca12..5bde8b5 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -90,6 +90,9 @@
// Is key dispatching paused for this token?
boolean paused = false;
+ /** Whether this container should be removed when it no longer animates. */
+ boolean mIsExiting;
+
/** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
final boolean mOwnerCanManageAppTokens;
@@ -276,6 +279,28 @@
}
}
+ @Override
+ void removeIfPossible() {
+ if (mTransitionController.isPlayingTarget(this)) {
+ // Defer removing this container until the transition is finished. So the removal can
+ // execute after the finish transaction (see Transition#buildFinishTransaction) which
+ // may reparent it to original parent.
+ mIsExiting = true;
+ return;
+ }
+ mIsExiting = false;
+ removeAllWindowsIfPossible();
+ removeImmediately();
+ }
+
+ @Override
+ boolean handleCompleteDeferredRemoval() {
+ if (mIsExiting) {
+ removeIfPossible();
+ }
+ return super.handleCompleteDeferredRemoval();
+ }
+
/**
* @return The scale for applications running in compatibility mode. Multiply the size in the
* application by this scale will be the size in the screen.
@@ -725,6 +750,9 @@
pw.print("fixedRotationConfig=");
pw.println(mFixedRotationTransformState.mRotatedOverrideConfiguration);
}
+ if (mIsExiting) {
+ pw.print(prefix); pw.println("isExiting=true");
+ }
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 714eb4b..35328a0e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
@@ -157,7 +158,16 @@
// Verify that the other token window is still around.
assertEquals(1, token.getWindowsCount());
+ final TransitionController transitionController = token.mTransitionController;
+ spyOn(transitionController);
+ doReturn(true).when(transitionController).isPlayingTarget(token);
window2.removeImmediately();
+ assertTrue(token.mIsExiting);
+ assertNotNull("Defer removal for playing transition", token.getParent());
+
+ doReturn(false).when(transitionController).isPlayingTarget(token);
+ token.handleCompleteDeferredRemoval();
+ assertFalse(token.mIsExiting);
// Verify that the token is no-longer attached to its parent
assertNull(token.getParent());
// Verify that the token windows are no longer attached to it.