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

Commit 7c6f6b80 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Defer rotation update during recent transition

This migrates the changes [1] and [2] to shell transition.

[1] I3f2a46ddfe5e76ad3200ea27ad272ae36b6bcc8d
[2] I5e7487b08e139b251d160bd0627326b83351640e

With shell transition+fixed rotation, even if the home is moved to
top for showing recents overview, a rotation change may still happen
if the display is in non-natural rotation (different than NOSENSOR).
So the legacy condition is still needed to defer the rotation change.

Eliminate the post message for updating rotation by setting
the orientation of recents mLastOrientation. So when finishing
the transition, the change can be seen:
 Legacy: handleAppTransitionReady -> FINISH_LAYOUT_REDO_CONFIG
 Shell: onAppTransitionFinishedLocked
        -> continueUpdateOrientationForDiffOrienLaunchingApp

Bug: 216169338
Test: atest TransitionTests#testDeferRotationForTransientLaunch
      RecentsAnimationControllerTest#testCheckRotationAfterCleanup

Change-Id: I2580c6d7b46a641abd4d8f963e915122af215ac7
parent 91e92eb0
Loading
Loading
Loading
Loading
+21 −6
Original line number 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
         * 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
+8 −12
Original line number Diff line number Diff line
@@ -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.
@@ -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;
            }
        }
@@ -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(),
+0 −23
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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() {
+17 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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
+40 −0
Original line number 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.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;
@@ -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;
@@ -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);