From 36fd1663024b3c39d25b6758a789daa046ff998a Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 29 Sep 2021 13:45:12 -0600 Subject: Check pending rotation change after recents animation Rotation change was skipped while running recents animation to avoid interrupting the animation and may apply unexpected intermediate orientation to recents activity. But the rotation change should be still applied after the animation is finished. Otherwise the display may stay in old rotation. The case may be more obvious since live tile is introduced, because the animation keeps running until switch to app. E.g. enable auto rotation and the top is a rotatable app in landscape, and then enter recents and rotate the device to portrait. After going back to the current app, the display is still in landscape. Fix: 199371058 Test: RecentsAnimationControllerTest#testCheckRotationAfterCleanup Change-Id: I5e7487b08e139b251d160bd0627326b83351640e --- .../com/android/server/wm/DisplayRotation.java | 5 ++++- .../server/wm/RecentsAnimationController.java | 23 ++++++++++++++++++++++ .../com/android/server/wm/DisplayContentTests.java | 3 ++- .../server/wm/RecentsAnimationControllerTest.java | 18 ++++++++++++++++- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 971bebd8c486..225a6ea20f3d 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -442,7 +442,9 @@ public class DisplayRotation { return false; } - if (mDisplayContent.mFixedRotationTransitionListener + final RecentsAnimationController recentsAnimController = + mService.getRecentsAnimationController(); + if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener .isTopFixedOrientationRecentsAnimating() // If screen is off or the device is going to sleep, then still allow to update. && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) { @@ -450,6 +452,7 @@ public class DisplayRotation { // In order to ignore its requested orientation to avoid a sensor led rotation (e.g // user rotating the device while the recents animation is running), we ignore // rotation update while the animation is running. + recentsAnimController.setCheckRotationAfterCleanup(); return false; } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index ba85c9800e56..03ff06c9d7f1 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -123,6 +123,7 @@ public class RecentsAnimationController implements DeathRecipient { private final int mDisplayId; private boolean mWillFinishToHome = false; private final Runnable mFailsafeRunnable = this::onFailsafe; + private Runnable mCheckRotationAfterCleanup; // The recents component app token that is shown behind the visibile tasks private ActivityRecord mTargetActivityRecord; @@ -920,6 +921,24 @@ public class RecentsAnimationController implements DeathRecipient { mCancelDeferredWithScreenshot = screenshot; } + /** + * If the display rotation change is ignored while recents animation is running, make sure that + * the pending rotation change will be applied after the animation finishes. + */ + void setCheckRotationAfterCleanup() { + if (mCheckRotationAfterCleanup != null) return; + mCheckRotationAfterCleanup = () -> { + synchronized (mService.mGlobalLock) { + if (mDisplayContent.getDisplayRotation() + .updateRotationAndSendNewConfigIfChanged()) { + if (mTargetActivityRecord != null) { + mTargetActivityRecord.finishFixedRotationTransform(); + } + } + } + }; + } + /** * @return Whether we should defer the cancel from a root task order change until the next app * transition. @@ -1007,6 +1026,10 @@ public class RecentsAnimationController implements DeathRecipient { if (mStatusBar != null) { mStatusBar.onRecentsAnimationStateChanged(false /* running */); } + if (mCheckRotationAfterCleanup != null) { + mService.mH.post(mCheckRotationAfterCleanup); + mCheckRotationAfterCleanup = null; + } } void scheduleFailsafe() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index e3402177140d..24bbf4682157 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1562,6 +1562,7 @@ public class DisplayContentTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(mDisplayContent); final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent); recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController(); // Do not rotate if the recents animation is animating on top. mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity); @@ -2513,7 +2514,7 @@ public class DisplayContentTests extends WindowTestsBase { assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); } - private static int getRotatedOrientation(DisplayContent dc) { + static int getRotatedOrientation(DisplayContent dc) { return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index d88fbee6ae13..a680cba8b266 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -41,8 +41,8 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -438,6 +438,22 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertTrue(activity.hasFixedRotationTransform()); } + @Test + public void testCheckRotationAfterCleanup() { + mWm.setRecentsAnimationController(mController); + spyOn(mDisplayContent.mFixedRotationTransitionListener); + doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener) + .isTopFixedOrientationRecentsAnimating(); + // Rotation update is skipped while the recents animation is running. + assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests + .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */)); + final int prevRotation = mDisplayContent.getRotation(); + mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); + waitHandlerIdle(mWm.mH); + // The display should be updated to the changed orientation after the animation is finished. + assertNotEquals(mDisplayContent.getRotation(), prevRotation); + } + @Test public void testWallpaperHasFixedRotationApplied() { unblockDisplayRotation(mDefaultDisplay); -- cgit v1.2.3-59-g8ed1b