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

Commit bc9e4bec authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

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

parents 3a1c70f6 7c6f6b80
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);