From 242be9051657dd25ad9943be18ba34caac6e40a2 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 3 Jun 2020 12:24:45 +0800 Subject: [PATCH] Wait for all of associated activities done animating Fixed rotation transform was finished and continued to update the orientation when transferring the starting window, which was before the activity ready to show and caused flickers. This can happen while trampoline activities were started during app launch. This also fixes other flickers that happens when the fixed rotation launching app's animation finished before other associated activities. Bug: 157446341 Test: atest DisplayContentTests AppWindowTokenTests Change-Id: Ibeb50edc7dfd9dddffe2c420fde92c1ebb70ed37 --- .../com/android/server/wm/ActivityRecord.java | 7 +++++ .../com/android/server/wm/DisplayContent.java | 6 ++++- .../com/android/server/wm/WindowToken.java | 19 +++++++++++++ .../server/wm/AppWindowTokenTests.java | 27 +++++++++++++++++++ .../server/wm/DisplayContentTests.java | 7 +++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 01eb9c5cb3d9f..b545cf439879b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3355,6 +3355,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final long origId = Binder.clearCallingIdentity(); try { + // Link the fixed rotation transform to this activity since we are transferring the + // starting window. + if (fromActivity.hasFixedRotationTransform()) { + mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(this, + false /* checkOpening */); + } + // Transfer the starting window over to the new token. mStartingData = fromActivity.mStartingData; startingSurface = fromActivity.startingSurface; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4e19a5224bb4f..78b28e4ebe658 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5671,7 +5671,11 @@ class DisplayContent extends WindowContainer { notifyFixedRotationTransform(true /* enabled */); } + /** + * Return {@code true} if one of the associated activity is still animating. Otherwise, + * return {@code false}. + */ + boolean hasAnimatingFixedRotationTransition() { + if (mFixedRotationTransformState == null) { + return false; + } + + for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) { + final ActivityRecord r = + mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord(); + if (r != null && r.isAnimating(TRANSITION | PARENTS)) { + return true; + } + } + return false; + } + void finishFixedRotationTransform() { finishFixedRotationTransform(null /* applyDisplayRotation */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index ef28a450695be..a16bd2a72a833 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -38,6 +38,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; @@ -452,6 +453,32 @@ public class AppWindowTokenTests extends WindowTestsBase { assertFalse(middle.isVisible()); } + @Test + public void testTransferStartingWindowSetFixedRotation() { + mWm.mIsFixedRotationTransformEnabled = true; + final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask); + mTask.positionChildAt(topActivity, POSITION_TOP); + mActivity.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false); + waitUntilHandlersIdle(); + + // Make activities to have different rotation from it display and set fixed rotation + // transform to activity1. + int rotation = (mDisplayContent.getRotation() + 1) % 4; + mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation); + doReturn(rotation).when(mDisplayContent) + .rotationForActivityInDifferentOrientation(topActivity); + + // Make sure the fixed rotation transform linked to activity2 when adding starting window + // on activity2. + topActivity.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(), + false, false, false, true, false); + waitUntilHandlersIdle(); + assertTrue(topActivity.hasFixedRotationTransform()); + } + private ActivityRecord createIsolatedTestActivityRecord() { final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(taskStack, 0 /* userId */); 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 4ad7dff870722..d3f677ced3294 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1144,7 +1144,14 @@ public class DisplayContentTests extends WindowTestsBase { assertTrue(app.hasFixedRotationTransform(app2)); assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2)); + // The fixed rotation transform can only be finished when all animation finished. + doReturn(false).when(app2).isAnimating(anyInt(), anyInt()); + mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token); + assertTrue(app.hasFixedRotationTransform()); + assertTrue(app2.hasFixedRotationTransform()); + // The display should be rotated after the launch is finished. + doReturn(false).when(app).isAnimating(anyInt(), anyInt()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); // The fixed rotation should be cleared and the new rotation is applied to display.