From 442aff2d0b82790a08b792990d625c10a63f9570 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Tue, 7 May 2019 19:49:31 +0800 Subject: [PATCH] Fix window didn't disappear if enable/disable ime at same time When starting to freeze display, AppTransition also clear all status and data, but if the RemoteAnimation didn't start. The window can't recevice the finish animation callback, that would make the state is always EXITING. Bug: 130618911 Test: atest DocumentsTest Test: atest WmTests:RemoteAnimationControllerTest Test: atest WmTests:AppTransitionTests Change-Id: I5695a291f26323eb78cced435722b459b963a9f1 --- .../com/android/server/wm/AppTransition.java | 7 ++ .../server/wm/RemoteAnimationController.java | 2 +- .../android/server/wm/AppTransitionTests.java | 68 +++++++++++++++++-- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index e053ff338a25f..0915caedd6bc7 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -452,6 +452,13 @@ public class AppTransition implements Dump { void freeze() { final int transit = mNextAppTransition; + // The RemoteAnimationControl didn't register AppTransitionListener and + // only initialized the finish and timeout callback when goodToGo(). + // So cancel the remote animation here to prevent the animation can't do + // finish after transition state cleared. + if (mRemoteAnimationController != null) { + mRemoteAnimationController.cancelAnimation("freeze"); + } setAppTransition(TRANSIT_UNSET, 0 /* flags */); clear(); setReady(); diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 5f95691d53075..b4bfedda94c7c 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -132,7 +132,7 @@ class RemoteAnimationController implements DeathRecipient { sendRunningRemoteAnimation(true); } - private void cancelAnimation(String reason) { + void cancelAnimation(String reason) { if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason); synchronized (mService.getWindowManagerLock()) { if (mCanceled) { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 5fc7f44588e15..5a72a584b122e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -18,30 +18,40 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.graphics.Rect; +import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Display; - -import org.junit.Before; -import org.junit.Test; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationTarget; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import org.junit.Before; +import org.junit.Test; + /** * Test class for {@link AppTransition}. * @@ -51,7 +61,6 @@ import androidx.test.filters.SmallTest; @SmallTest @Presubmit public class AppTransitionTests extends WindowTestsBase { - private DisplayContent mDc; @Before @@ -181,4 +190,55 @@ public class AppTransitionTests extends WindowTestsBase { getInstrumentation().getTargetContext(), -1)); } + @Test + public void testCancelRemoteAnimationWhenFreeze() { + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + dc, "exiting app"); + final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; + // Wait until everything in animation handler get executed to prevent the exiting window + // from being removed during WindowSurfacePlacer Traversal. + waitUntilHandlersIdle(); + + // Set a remote animator. + final TestRemoteAnimationRunner runner = new TestRemoteAnimationRunner(); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + runner, 100, 50, true /* changeNeedsSnapshot */); + // RemoteAnimationController will tracking RemoteAnimationAdapter's caller with calling pid. + adapter.setCallingPid(123); + + // Simulate activity finish flows to prepare app transition & set visibility, + // make sure transition is set as expected. + dc.prepareAppTransition(TRANSIT_ACTIVITY_CLOSE, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransition()); + dc.mAppTransition.overridePendingAppTransitionRemote(adapter); + exitingAppToken.setVisibility(false, false); + assertTrue(dc.mClosingApps.size() > 0); + + // Make sure window is in animating stage before freeze, and cancel after freeze. + assertTrue(dc.isAppAnimating()); + assertFalse(runner.mCancelled); + dc.mAppTransition.freeze(); + assertFalse(dc.isAppAnimating()); + assertTrue(runner.mCancelled); + } + + private class TestRemoteAnimationRunner implements IRemoteAnimationRunner { + boolean mCancelled = false; + @Override + public void onAnimationStart(RemoteAnimationTarget[] apps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + } + + @Override + public void onAnimationCancelled() { + mCancelled = true; + } + + @Override + public IBinder asBinder() { + return null; + } + } }