Merge "Avoid recursive destruction from exit animation done" into tm-dev

This commit is contained in:
Riddle Hsu
2022-06-08 01:02:14 +00:00
committed by Android (Google) Code Review
2 changed files with 49 additions and 9 deletions

View File

@@ -2433,14 +2433,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override @Override
void removeImmediately() { void removeImmediately() {
if (!mRemoved) {
// Destroy surface before super call. The general pattern is that the children need
// to be removed before the parent (so that the sync-engine tracking works). Since
// WindowStateAnimator is a "virtual" child, we have to do it manually here.
mWinAnimator.destroySurfaceLocked(getSyncTransaction());
}
super.removeImmediately();
if (mRemoved) { if (mRemoved) {
// Nothing to do. // Nothing to do.
ProtoLog.v(WM_DEBUG_ADD_REMOVE, ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -2449,6 +2441,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} }
mRemoved = true; mRemoved = true;
// Destroy surface before super call. The general pattern is that the children need
// to be removed before the parent (so that the sync-engine tracking works). Since
// WindowStateAnimator is a "virtual" child, we have to do it manually here.
mWinAnimator.destroySurfaceLocked(getSyncTransaction());
super.removeImmediately();
mWillReplaceWindow = false; mWillReplaceWindow = false;
if (mReplacementWindow != null) { if (mReplacementWindow != null) {
@@ -5090,7 +5087,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Otherwise we add the service to mDestroySurface and allow it to be processed in our next // Otherwise we add the service to mDestroySurface and allow it to be processed in our next
// transaction. // transaction.
if (mActivityRecord != null) { if (mActivityRecord != null) {
if (mAttrs.type == TYPE_BASE_APPLICATION) {
mActivityRecord.destroySurfaces(); mActivityRecord.destroySurfaces();
} else {
destroySurface(false /* cleanupOnResume */, mActivityRecord.mAppStopped);
}
} else { } else {
if (hasSurface) { if (hasSurface) {
mWmService.mDestroySurface.add(this); mWmService.mDestroySurface.add(this);

View File

@@ -74,6 +74,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -471,6 +472,44 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(appWindow.mRemoved); assertTrue(appWindow.mRemoved);
} }
@Test
public void testOnExitAnimationDone() {
final WindowState parent = createWindow(null, TYPE_APPLICATION, "parent");
final WindowState child = createWindow(parent, TYPE_APPLICATION_PANEL, "child");
final SurfaceControl.Transaction t = parent.getPendingTransaction();
child.startAnimation(t, mock(AnimationAdapter.class), false /* hidden */,
SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
parent.mAnimatingExit = parent.mRemoveOnExit = parent.mWindowRemovalAllowed = true;
child.mAnimatingExit = child.mRemoveOnExit = child.mWindowRemovalAllowed = true;
final int[] numRemovals = new int[2];
parent.registerWindowContainerListener(new WindowContainerListener() {
@Override
public void onRemoved() {
numRemovals[0]++;
}
});
child.registerWindowContainerListener(new WindowContainerListener() {
@Override
public void onRemoved() {
numRemovals[1]++;
}
});
spyOn(parent);
// parent onExitAnimationDone
// -> child onExitAnimationDone() -> no-op because isAnimating()
// -> parent destroySurface()
// -> parent removeImmediately() because mDestroying+mRemoveOnExit
// -> child removeImmediately() -> cancelAnimation()
// -> child onExitAnimationDone()
// -> child destroySurface() because animation is canceled
// -> child removeImmediately() -> no-op because mRemoved
parent.onExitAnimationDone();
// There must be no additional destroySurface() of parent from its child.
verify(parent, atMost(1)).destroySurface(anyBoolean(), anyBoolean());
assertEquals(1, numRemovals[0]);
assertEquals(1, numRemovals[1]);
}
@Test @Test
public void testLayoutSeqResetOnReparent() { public void testLayoutSeqResetOnReparent() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app");