Loading services/core/java/com/android/server/wm/DisplayContent.java +21 −6 Original line number Diff line number Diff line Loading @@ -6368,13 +6368,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** * Return {@code true} if there is an ongoing animation to the "Recents" activity and this * activity as a fixed orientation so shouldn't be rotated. * Returns the fixed orientation requested by a transient launch (e.g. recents animation). * If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred. */ boolean isTopFixedOrientationRecentsAnimating() { return mAnimatingRecents != null && mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */) != ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp(); @ActivityInfo.ScreenOrientation int getTransientFixedOrientation() { ActivityRecord source = null; if (mTransitionController.isShellTransitionsEnabled()) { final ActivityRecord r = mFixedRotationLaunchingApp; if (r != null && mTransitionController.isTransientLaunch(r)) { source = r; } } else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) { source = mAnimatingRecents; } if (source == null || source.getRequestedConfigurationOrientation( true /* forDisplay */) == ORIENTATION_UNDEFINED) { return SCREEN_ORIENTATION_UNSET; } if (!mWmService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) { // If screen is off or the device is going to sleep, then still allow to update. return SCREEN_ORIENTATION_UNSET; } return source.mOrientation; } @Override Loading services/core/java/com/android/server/wm/DisplayRotation.java +8 −12 Original line number Diff line number Diff line Loading @@ -419,11 +419,8 @@ public class DisplayRotation { * THE SCREEN. */ boolean updateRotationUnchecked(boolean forceUpdate) { final boolean useShellTransitions = mDisplayContent.mTransitionController.isShellTransitionsEnabled(); final int displayId = mDisplayContent.getDisplayId(); if (!forceUpdate && !useShellTransitions) { if (!forceUpdate) { if (mDeferredRotationPauseCount > 0) { // Rotation updates have been paused temporarily. Defer the update until updates // have been resumed. Loading @@ -449,17 +446,16 @@ public class DisplayRotation { return false; } 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 */)) { final int transientFixedOrientation = mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation(); if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) { // Makes sure that after the transition is finished, updateOrientation() can see // the difference from the latest orientation source. mLastOrientation = transientFixedOrientation; // During the recents animation, the closing app might still be considered on top. // 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; } } Loading Loading @@ -513,7 +509,7 @@ public class DisplayRotation { mDisplayContent.setLayoutNeeded(); if (useShellTransitions) { if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting(); final TransitionRequestInfo.DisplayChange change = wasCollecting ? null : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(), Loading services/core/java/com/android/server/wm/RecentsAnimationController.java +0 −23 Original line number Diff line number Diff line Loading @@ -122,7 +122,6 @@ 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; Loading Loading @@ -919,24 +918,6 @@ 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. Loading Loading @@ -1037,10 +1018,6 @@ public class RecentsAnimationController implements DeathRecipient { if (mStatusBar != null) { mStatusBar.onRecentsAnimationStateChanged(false /* running */); } if (mCheckRotationAfterCleanup != null) { mService.mH.post(mCheckRotationAfterCleanup); mCheckRotationAfterCleanup = null; } } void scheduleFailsafe() { Loading services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +17 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; Loading Loading @@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { public void testCheckRotationAfterCleanup() { mWm.setRecentsAnimationController(mController); spyOn(mDisplayContent.mFixedRotationTransitionListener); doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener) .isTopFixedOrientationRecentsAnimating(); final ActivityRecord recents = mock(ActivityRecord.class); recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; doReturn(ORIENTATION_PORTRAIT).when(recents) .getRequestedConfigurationOrientation(anyBoolean()); mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents); // Rotation update is skipped while the recents animation is running. assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */)); final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay); assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */)); assertEquals(recents.mOrientation, displayRotation.getLastOrientation()); final int prevRotation = mDisplayContent.getRotation(); mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); waitHandlerIdle(mWm.mH); // In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout // -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent# // applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG. assertTrue(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */)); // The display should be updated to the changed orientation after the animation is finished. assertNotEquals(mDisplayContent.getRotation(), prevRotation); assertNotEquals(displayRotation.getRotation(), prevRotation); } @Test Loading services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +40 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; Loading @@ -33,13 +34,17 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.isIndependent; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 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.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; Loading Loading @@ -659,6 +664,41 @@ public class TransitionTests extends WindowTestsBase { assertNull(mDisplayContent.getAsyncRotationController()); } @Test public void testDeferRotationForTransientLaunch() { final TestTransitionPlayer player = registerTestTransitionPlayer(); assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation()); final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord home = new ActivityBuilder(mAtm) .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask()) .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build(); final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN); final int prevRotation = mDisplayContent.getRotation(); transition.setTransientLaunch(home, null /* restoreBelow */); home.mTransitionController.requestStartTransition(transition, home.getTask(), null /* remoteTransition */, null /* displayChange */); transition.collectExistenceChange(home); home.mVisibleRequested = true; mDisplayContent.setFixedRotationLaunchingAppUnchecked(home); doReturn(true).when(home).hasFixedRotationTransform(any()); player.startTransition(); player.onTransactionReady(mDisplayContent.getSyncTransaction()); final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); spyOn(displayRotation); doReturn(true).when(displayRotation).isWaitingForRemoteRotation(); doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Rotation update is skipped while the recents animation is running. assertFalse(mDisplayContent.updateRotationUnchecked()); assertEquals(SCREEN_ORIENTATION_NOSENSOR, displayRotation.getLastOrientation()); // Return to the app without fixed orientation from recents. app.moveFocusableActivityToTop("test"); player.finish(); // The display should be updated to the changed orientation after the animation is finish. assertNotEquals(mDisplayContent.getRotation(), prevRotation); } @Test public void testIntermediateVisibility() { final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); Loading Loading
services/core/java/com/android/server/wm/DisplayContent.java +21 −6 Original line number Diff line number Diff line Loading @@ -6368,13 +6368,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** * Return {@code true} if there is an ongoing animation to the "Recents" activity and this * activity as a fixed orientation so shouldn't be rotated. * Returns the fixed orientation requested by a transient launch (e.g. recents animation). * If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred. */ boolean isTopFixedOrientationRecentsAnimating() { return mAnimatingRecents != null && mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */) != ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp(); @ActivityInfo.ScreenOrientation int getTransientFixedOrientation() { ActivityRecord source = null; if (mTransitionController.isShellTransitionsEnabled()) { final ActivityRecord r = mFixedRotationLaunchingApp; if (r != null && mTransitionController.isTransientLaunch(r)) { source = r; } } else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) { source = mAnimatingRecents; } if (source == null || source.getRequestedConfigurationOrientation( true /* forDisplay */) == ORIENTATION_UNDEFINED) { return SCREEN_ORIENTATION_UNSET; } if (!mWmService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) { // If screen is off or the device is going to sleep, then still allow to update. return SCREEN_ORIENTATION_UNSET; } return source.mOrientation; } @Override Loading
services/core/java/com/android/server/wm/DisplayRotation.java +8 −12 Original line number Diff line number Diff line Loading @@ -419,11 +419,8 @@ public class DisplayRotation { * THE SCREEN. */ boolean updateRotationUnchecked(boolean forceUpdate) { final boolean useShellTransitions = mDisplayContent.mTransitionController.isShellTransitionsEnabled(); final int displayId = mDisplayContent.getDisplayId(); if (!forceUpdate && !useShellTransitions) { if (!forceUpdate) { if (mDeferredRotationPauseCount > 0) { // Rotation updates have been paused temporarily. Defer the update until updates // have been resumed. Loading @@ -449,17 +446,16 @@ public class DisplayRotation { return false; } 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 */)) { final int transientFixedOrientation = mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation(); if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) { // Makes sure that after the transition is finished, updateOrientation() can see // the difference from the latest orientation source. mLastOrientation = transientFixedOrientation; // During the recents animation, the closing app might still be considered on top. // 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; } } Loading Loading @@ -513,7 +509,7 @@ public class DisplayRotation { mDisplayContent.setLayoutNeeded(); if (useShellTransitions) { if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting(); final TransitionRequestInfo.DisplayChange change = wasCollecting ? null : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(), Loading
services/core/java/com/android/server/wm/RecentsAnimationController.java +0 −23 Original line number Diff line number Diff line Loading @@ -122,7 +122,6 @@ 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; Loading Loading @@ -919,24 +918,6 @@ 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. Loading Loading @@ -1037,10 +1018,6 @@ public class RecentsAnimationController implements DeathRecipient { if (mStatusBar != null) { mStatusBar.onRecentsAnimationStateChanged(false /* running */); } if (mCheckRotationAfterCleanup != null) { mService.mH.post(mCheckRotationAfterCleanup); mCheckRotationAfterCleanup = null; } } void scheduleFailsafe() { Loading
services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +17 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; Loading Loading @@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { public void testCheckRotationAfterCleanup() { mWm.setRecentsAnimationController(mController); spyOn(mDisplayContent.mFixedRotationTransitionListener); doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener) .isTopFixedOrientationRecentsAnimating(); final ActivityRecord recents = mock(ActivityRecord.class); recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; doReturn(ORIENTATION_PORTRAIT).when(recents) .getRequestedConfigurationOrientation(anyBoolean()); mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents); // Rotation update is skipped while the recents animation is running. assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */)); final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay); assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */)); assertEquals(recents.mOrientation, displayRotation.getLastOrientation()); final int prevRotation = mDisplayContent.getRotation(); mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); waitHandlerIdle(mWm.mH); // In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout // -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent# // applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG. assertTrue(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */)); // The display should be updated to the changed orientation after the animation is finished. assertNotEquals(mDisplayContent.getRotation(), prevRotation); assertNotEquals(displayRotation.getRotation(), prevRotation); } @Test Loading
services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +40 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; Loading @@ -33,13 +34,17 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.isIndependent; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 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.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; Loading Loading @@ -659,6 +664,41 @@ public class TransitionTests extends WindowTestsBase { assertNull(mDisplayContent.getAsyncRotationController()); } @Test public void testDeferRotationForTransientLaunch() { final TestTransitionPlayer player = registerTestTransitionPlayer(); assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation()); final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord home = new ActivityBuilder(mAtm) .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask()) .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build(); final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN); final int prevRotation = mDisplayContent.getRotation(); transition.setTransientLaunch(home, null /* restoreBelow */); home.mTransitionController.requestStartTransition(transition, home.getTask(), null /* remoteTransition */, null /* displayChange */); transition.collectExistenceChange(home); home.mVisibleRequested = true; mDisplayContent.setFixedRotationLaunchingAppUnchecked(home); doReturn(true).when(home).hasFixedRotationTransform(any()); player.startTransition(); player.onTransactionReady(mDisplayContent.getSyncTransaction()); final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); spyOn(displayRotation); doReturn(true).when(displayRotation).isWaitingForRemoteRotation(); doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Rotation update is skipped while the recents animation is running. assertFalse(mDisplayContent.updateRotationUnchecked()); assertEquals(SCREEN_ORIENTATION_NOSENSOR, displayRotation.getLastOrientation()); // Return to the app without fixed orientation from recents. app.moveFocusableActivityToTop("test"); player.finish(); // The display should be updated to the changed orientation after the animation is finish. assertNotEquals(mDisplayContent.getRotation(), prevRotation); } @Test public void testIntermediateVisibility() { final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); Loading