From d4957437b7eabb42c6887a63b3b38df803cb5061 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 24 Apr 2020 22:06:29 +0800 Subject: [PATCH 1/3] 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()); From cbc5d2721b8f97e619f4bcf63cb6c58c6f2e7580 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 24 Apr 2020 23:26:46 +0800 Subject: [PATCH 2/3] Polish fixed rotated recents animation For the cases that move recents or home to top, the transformed state is kept until the transition of task-to-front is done. This fixes the flickering when switching between recents and the activity that supported PiP. Also eliminated an additional toggle of fixed rotation transform from finishing recents animation. Fixes: 154588225 Test: atest RecentsAnimationControllerTest Change-Id: I92c6dfac1d030f76f62a10bd0596f935adffb63c --- .../com/android/server/wm/DisplayContent.java | 60 +++++++++++++++++-- .../server/wm/RecentsAnimationController.java | 11 ++-- .../wm/RecentsAnimationControllerTest.java | 18 +++++- 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5b2ccf43833da..a1620fe410c77 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -494,6 +494,7 @@ class DisplayContent extends WindowContainer Date: Sat, 25 Apr 2020 01:58:23 +0800 Subject: [PATCH 3/3] Clear fixed rotation if display config does not change There is a timing that when remote rotation is completed, the orientation from sensor has been updated again. If the transition of rotated app hasn't complete, the display is still in previous orientation, then if it is the same as the last value from sensor, the display configuration does not change. So in this case the rotated app needs to be restored to avoid showing different orientation than the display. Fixes: 153327533 Test: ActivityRecordTests#testActivityOnCancelFixedRotationTransform Test: Add delay to execute DisplayRotation#continueRotation. Put device in landscape with a portrait home on top, launch an activity without fixed orientation, and then rotate the device to portrait immediately. The activity should launch in landscape and rotate to portrait with animation. Change-Id: Ic966923a751d94cc6ea86a83c2f600424999799c --- .../com/android/server/wm/DisplayContent.java | 25 +++++++++++++++++-- .../server/wm/ActivityRecordTests.java | 14 ++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a1620fe410c77..c66ff330edbdf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1271,6 +1271,12 @@ class DisplayContent extends WindowContainer