Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acfe6767 authored by Riddle Hsu's avatar Riddle Hsu Committed by Automerger Merge Worker
Browse files

Merge "Defer rotation update during recent transition" into tm-dev am: bc9e4bec

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16966123

Change-Id: I9224bb70bdf4fe3b91a9ddb60bd214a37992b392
parents d38feaea bc9e4bec
Loading
Loading
Loading
Loading
+21 −6
Original line number Original line Diff line number Diff line
@@ -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
         * Returns the fixed orientation requested by a transient launch (e.g. recents animation).
         * activity as a fixed orientation so shouldn't be rotated.
         * If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred.
         */
         */
        boolean isTopFixedOrientationRecentsAnimating() {
        @ActivityInfo.ScreenOrientation int getTransientFixedOrientation() {
            return mAnimatingRecents != null
            ActivityRecord source = null;
                    && mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */)
            if (mTransitionController.isShellTransitionsEnabled()) {
                    != ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp();
                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
        @Override
+8 −12
Original line number Original line Diff line number Diff line
@@ -419,11 +419,8 @@ public class DisplayRotation {
     *         THE SCREEN.
     *         THE SCREEN.
     */
     */
    boolean updateRotationUnchecked(boolean forceUpdate) {
    boolean updateRotationUnchecked(boolean forceUpdate) {
        final boolean useShellTransitions =
                mDisplayContent.mTransitionController.isShellTransitionsEnabled();

        final int displayId = mDisplayContent.getDisplayId();
        final int displayId = mDisplayContent.getDisplayId();
        if (!forceUpdate && !useShellTransitions) {
        if (!forceUpdate) {
            if (mDeferredRotationPauseCount > 0) {
            if (mDeferredRotationPauseCount > 0) {
                // Rotation updates have been paused temporarily. Defer the update until updates
                // Rotation updates have been paused temporarily. Defer the update until updates
                // have been resumed.
                // have been resumed.
@@ -449,17 +446,16 @@ public class DisplayRotation {
                return false;
                return false;
            }
            }


            final RecentsAnimationController recentsAnimController =
            final int transientFixedOrientation =
                    mService.getRecentsAnimationController();
                    mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation();
            if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
            if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) {
                    .isTopFixedOrientationRecentsAnimating()
                // Makes sure that after the transition is finished, updateOrientation() can see
                    // If screen is off or the device is going to sleep, then still allow to update.
                // the difference from the latest orientation source.
                    && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
                mLastOrientation = transientFixedOrientation;
                // During the recents animation, the closing app might still be considered on top.
                // 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
                // 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
                // user rotating the device while the recents animation is running), we ignore
                // rotation update while the animation is running.
                // rotation update while the animation is running.
                recentsAnimController.setCheckRotationAfterCleanup();
                return false;
                return false;
            }
            }
        }
        }
@@ -513,7 +509,7 @@ public class DisplayRotation {


        mDisplayContent.setLayoutNeeded();
        mDisplayContent.setLayoutNeeded();


        if (useShellTransitions) {
        if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
            final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
            final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
            final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
            final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
                    : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
                    : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
+0 −23
Original line number Original line Diff line number Diff line
@@ -122,7 +122,6 @@ public class RecentsAnimationController implements DeathRecipient {
    private final int mDisplayId;
    private final int mDisplayId;
    private boolean mWillFinishToHome = false;
    private boolean mWillFinishToHome = false;
    private final Runnable mFailsafeRunnable = this::onFailsafe;
    private final Runnable mFailsafeRunnable = this::onFailsafe;
    private Runnable mCheckRotationAfterCleanup;


    // The recents component app token that is shown behind the visibile tasks
    // The recents component app token that is shown behind the visibile tasks
    private ActivityRecord mTargetActivityRecord;
    private ActivityRecord mTargetActivityRecord;
@@ -919,24 +918,6 @@ public class RecentsAnimationController implements DeathRecipient {
        mCancelDeferredWithScreenshot = screenshot;
        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
     * @return Whether we should defer the cancel from a root task order change until the next app
     * transition.
     * transition.
@@ -1037,10 +1018,6 @@ public class RecentsAnimationController implements DeathRecipient {
        if (mStatusBar != null) {
        if (mStatusBar != null) {
            mStatusBar.onRecentsAnimationStateChanged(false /* running */);
            mStatusBar.onRecentsAnimationStateChanged(false /* running */);
        }
        }
        if (mCheckRotationAfterCleanup != null) {
            mService.mH.post(mCheckRotationAfterCleanup);
            mCheckRotationAfterCleanup = null;
        }
    }
    }


    void scheduleFailsafe() {
    void scheduleFailsafe() {
+17 −6
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.wm;
package com.android.server.wm;


import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
    public void testCheckRotationAfterCleanup() {
    public void testCheckRotationAfterCleanup() {
        mWm.setRecentsAnimationController(mController);
        mWm.setRecentsAnimationController(mController);
        spyOn(mDisplayContent.mFixedRotationTransitionListener);
        spyOn(mDisplayContent.mFixedRotationTransitionListener);
        doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
        final ActivityRecord recents = mock(ActivityRecord.class);
                .isTopFixedOrientationRecentsAnimating();
        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.
        // Rotation update is skipped while the recents animation is running.
        assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
                .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
        final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
        assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
        assertEquals(recents.mOrientation, displayRotation.getLastOrientation());
        final int prevRotation = mDisplayContent.getRotation();
        final int prevRotation = mDisplayContent.getRotation();
        mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
        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.
        // The display should be updated to the changed orientation after the animation is finished.
        assertNotEquals(mDisplayContent.getRotation(), prevRotation);
        assertNotEquals(displayRotation.getRotation(), prevRotation);
    }
    }


    @Test
    @Test
+40 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -33,13 +34,17 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.isIndependent;
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.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
@@ -659,6 +664,41 @@ public class TransitionTests extends WindowTestsBase {
        assertNull(mDisplayContent.getAsyncRotationController());
        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
    @Test
    public void testIntermediateVisibility() {
    public void testIntermediateVisibility() {
        final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
        final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);