From d4957437b7eabb42c6887a63b3b38df803cb5061 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 24 Apr 2020 22:06:29 +0800 Subject: [PATCH] Extensive handling of fixed rotation launching app This fixes a case that display does not rotate after multiple activities in a different rotation than display are launched. - Link the transform state from the existing one to the new launching activity. So the heavy duplicated calculation is omitted and they can be recognized as a group to continue to update display rotation. - Add a dedicated transition listener so it will not miss to handle display rotation if the reported token is different. That avoids leakage if somehow the transition is not notified then the listener is not unregistered. The path to finish fixed rotation is also simplified because there is always a transition listener to handle the incoming record. Fixes: 154911677 Test: atest DisplayContentTests#testApplyTopFixedRotationTransform Change-Id: Id370d7980d11553905b6a051482e3765ed61dc39 --- .../com/android/server/wm/DisplayContent.java | 94 ++++++++++++------- .../com/android/server/wm/WindowToken.java | 36 +++---- .../server/wm/DisplayContentTests.java | 32 +++++-- .../wm/RecentsAnimationControllerTest.java | 4 - .../android/server/wm/WindowTokenTests.java | 4 +- 5 files changed, 101 insertions(+), 69 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c41029b206515..5b2ccf43833da 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -498,6 +498,9 @@ class DisplayContent extends WindowContainer mWinAddedSinceNullFocus = new ArrayList<>(); @@ -928,6 +931,7 @@ class DisplayContent extends WindowContainer applyRotation(oldRotation, newRotation)); mFixedRotationLaunchingApp = null; } @@ -5494,6 +5494,34 @@ class DisplayContent extends WindowContainer { * rotated by the given rotated display info, frames and insets. */ private static class FixedRotationTransformState { - final WindowToken mOwner; final DisplayInfo mDisplayInfo; final DisplayFrames mDisplayFrames; final InsetsState mInsetsState; @@ -133,10 +132,9 @@ class WindowToken extends WindowContainer { final ArrayList> mRotatedContainers = new ArrayList<>(3); boolean mIsTransforming = true; - FixedRotationTransformState(WindowToken owner, DisplayInfo rotatedDisplayInfo, + FixedRotationTransformState(DisplayInfo rotatedDisplayInfo, DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState, Configuration rotatedConfig, int currentRotation) { - mOwner = owner; mDisplayInfo = rotatedDisplayInfo; mDisplayFrames = rotatedDisplayFrames; mInsetsState = rotatedInsetsState; @@ -482,6 +480,14 @@ class WindowToken extends WindowContainer { return mFixedRotationTransformState != null; } + /** Returns {@code true} if the given token shares the same transform. */ + boolean hasFixedRotationTransform(WindowToken token) { + if (mFixedRotationTransformState == null || token == null) { + return false; + } + return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState; + } + boolean isFinishingFixedRotationTransform() { return mFixedRotationTransformState != null && !mFixedRotationTransformState.mIsTransforming; @@ -520,15 +526,14 @@ class WindowToken extends WindowContainer { final InsetsState insetsState = new InsetsState(); mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState, mDisplayContent.getConfiguration().uiMode); - mFixedRotationTransformState = new FixedRotationTransformState(this, info, displayFrames, + mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, insetsState, new Configuration(config), mDisplayContent.getRotation()); onConfigurationChanged(getParent().getConfiguration()); } /** * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this - * one. This takes the same effect as {@link #applyFixedRotationTransform}, but the linked state - * can only be cleared by the state owner. + * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { if (mFixedRotationTransformState != null) { @@ -543,28 +548,15 @@ class WindowToken extends WindowContainer { onConfigurationChanged(getParent().getConfiguration()); } - /** - * Finishes the transform and continue updating the orientation change of display. Only the - * state owner can finish the transform state. - */ void finishFixedRotationTransform() { - if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) { - return; - } - final boolean changed = - mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); - // If it is not the launching app or the display is not rotated, make sure the transform is - // cleared and the configuration is restored from parent. - if (!changed) { - clearFixedRotationTransform(null /* applyDisplayRotation */); - } + finishFixedRotationTransform(null /* applyDisplayRotation */); } /** - * Clears the transform and apply display rotation if the action is given. If the display will + * Finishes the transform and apply display rotation if the action is given. If the display will * not rotate, the transformed containers are restored to their original states. */ - void clearFixedRotationTransform(Runnable applyDisplayRotation) { + void finishFixedRotationTransform(Runnable applyDisplayRotation) { final FixedRotationTransformState state = mFixedRotationTransformState; if (state == null) { return; 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 e02ea813cdd7b..cba89d0aea2d2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -790,9 +790,7 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); - final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE - ? SCREEN_ORIENTATION_PORTRAIT - : SCREEN_ORIENTATION_LANDSCAPE; + final int newOrientation = getRotatedOrientation(dc); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer) @@ -812,9 +810,7 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); - final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE - ? SCREEN_ORIENTATION_PORTRAIT - : SCREEN_ORIENTATION_LANDSCAPE; + final int newOrientation = getRotatedOrientation(dc); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer) @@ -1083,7 +1079,8 @@ public class DisplayContentTests extends WindowTestsBase { mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); mDisplayContent.mOpeningApps.add(app); - app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final int newOrientation = getRotatedOrientation(mDisplayContent); + app.setRequestedOrientation(newOrientation); assertTrue(app.isFixedRotationTransforming()); assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly( @@ -1124,12 +1121,25 @@ public class DisplayContentTests extends WindowTestsBase { mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */); verify(t, never()).setPosition(any(), eq(0), eq(0)); + // Launch another activity before the transition is finished. + final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */); + mDisplayContent.mOpeningApps.add(app2); + app2.setRequestedOrientation(newOrientation); + + // The activity should share the same transform state as the existing one. + assertTrue(app.hasFixedRotationTransform(app2)); + + // The display should be rotated after the launch is finished. mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); // The animation in old rotation should be cancelled. assertFalse(closingApp.isAnimating()); - // The display should be rotated after the launch is finished. + // The fixed rotation should be cleared and the new rotation is applied to display. assertFalse(app.hasFixedRotationTransform()); + assertFalse(app2.hasFixedRotationTransform()); assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation); } @@ -1292,6 +1302,12 @@ public class DisplayContentTests extends WindowTestsBase { assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); } + private static int getRotatedOrientation(DisplayContent dc) { + return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; + } + private static List reverseList(List list) { final ArrayList result = new ArrayList<>(list); Collections.reverse(result); 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 f19550ced0bf7..409889b660b76 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -401,10 +401,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertEquals(Configuration.ORIENTATION_PORTRAIT, wallpapers.get(0).getConfiguration().orientation); - // Wallpaper's transform state is controlled by home, so the invocation should be no-op. - wallpaperWindowToken.finishFixedRotationTransform(); - assertTrue(wallpaperWindowToken.hasFixedRotationTransform()); - // Wallpaper's transform state should be cleared with home. homeActivity.finishFixedRotationTransform(); assertFalse(wallpaperWindowToken.hasFixedRotationTransform()); 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 535d53eeef719..23a097eb0c7ce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -136,7 +136,7 @@ public class WindowTokenTests extends WindowTestsBase { } @Test - public void testClearFixedRotationTransform() { + public void testFinishFixedRotationTransform() { final WindowToken appToken = mAppWindow.mToken; final WindowToken wallpaperToken = mWallpaperWindow.mToken; final Configuration config = new Configuration(mDisplayContent.getConfiguration()); @@ -152,7 +152,7 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.clearFixedRotationTransform(null /* applyDisplayRotation */); + mAppWindow.mToken.finishFixedRotationTransform(); // The window tokens should restore to the original rotation. assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation());