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

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

Merge changes Ic966923a,I92c6dfac,Id370d798 into rvc-dev

* changes:
  Clear fixed rotation if display config does not change
  Polish fixed rotated recents animation
  Extensive handling of fixed rotation launching app
parents e13fab2d b40f9662
Loading
Loading
Loading
Loading
+136 −35
Original line number Diff line number Diff line
@@ -494,10 +494,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     * The launching activity which is using fixed rotation transformation.
     *
     * @see #handleTopActivityLaunchingInDifferentOrientation
     * @see #setFixedRotationLaunchingApp
     * @see DisplayRotation#shouldRotateSeamlessly
     */
    ActivityRecord mFixedRotationLaunchingApp;

    final FixedRotationTransitionListener mFixedRotationTransitionListener =
            new FixedRotationTransitionListener();

    /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
    final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();

@@ -928,6 +932,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo

        mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
        mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
        mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
        mAppTransitionController = new AppTransitionController(mWmService, this);
        mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);

@@ -1266,6 +1271,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        if (configUpdated) {
            return;
        }

        // The display configuration doesn't change. If there is a launching transformed app, that
        // means its request to change display configuration has been discarded, then it should
        // respect to the current configuration of display.
        clearFixedRotationLaunchingApp();

        // Something changed (E.g. device rotation), but no configuration update is needed.
        // E.g. changing device rotation by 180 degrees. Go ahead and perform surface placement to
        // unfreeze the display since we froze it when the rotation was updated in
@@ -1383,7 +1394,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
                orientationSource != null ? orientationSource.asActivityRecord() : null;
        // Currently there is no use case from non-activity.
        if (r != null && handleTopActivityLaunchingInDifferentOrientation(r)) {
            mFixedRotationLaunchingApp = r;
            // Display orientation should be deferred until the top fixed rotation is finished.
            return false;
        }
@@ -1448,47 +1458,66 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            return false;
        }

        startFixedRotationTransform(r, rotation);
        mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() {
            void done() {
                r.finishFixedRotationTransform();
                mAppTransition.unregisterListener(this);
        setFixedRotationLaunchingApp(r, rotation);
        return true;
    }

            @Override
            public void onAppTransitionFinishedLocked(IBinder token) {
                if (token == r.token) {
                    done();
                }
    /**
     * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed
     * rotation transform to it and indicate that the display may be rotated after it is launched.
     */
    void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) {
        final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
        if (prevRotatedLaunchingApp != null
                && prevRotatedLaunchingApp.getWindowConfiguration().getRotation() == rotation
                // It is animating so we can expect there will have a transition callback.
                && prevRotatedLaunchingApp.isAnimating(TRANSITION | PARENTS)) {
            // It may be the case that multiple activities launch consecutively. Because their
            // rotation are the same, the transformed state can be shared to avoid duplicating
            // the heavy operations. This also benefits that the states of multiple activities
            // are handled together.
            r.linkFixedRotationTransform(prevRotatedLaunchingApp);
            return;
        }

            @Override
            public void onAppTransitionCancelledLocked(int transit) {
                done();
        if (!r.hasFixedRotationTransform()) {
            startFixedRotationTransform(r, rotation);
        }

            @Override
            public void onAppTransitionTimeoutLocked() {
                done();
        mFixedRotationLaunchingApp = r;
        if (prevRotatedLaunchingApp != null) {
            prevRotatedLaunchingApp.finishFixedRotationTransform();
        }
        });
        return true;
    }

    /** @return {@code true} if the display orientation will be changed. */
    boolean continueUpdateOrientationForDiffOrienLaunchingApp(WindowToken token) {
        if (token != mFixedRotationLaunchingApp) {
            return false;
    /**
     * Continue updating the orientation change of display if it was deferred by a top activity
     * launched in a different orientation.
     */
    void continueUpdateOrientationForDiffOrienLaunchingApp() {
        if (mFixedRotationLaunchingApp == null) {
            return;
        }
        // Update directly because the app which will change the orientation of display is ready.
        if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
            sendNewConfiguration();
            return true;
            return;
        }
        // The orientation of display is not changed.
        clearFixedRotationLaunchingApp();
    }

    /**
     * Clears the {@link mFixedRotationLaunchingApp} without applying rotation to display. It is
     * used when the display won't rotate (e.g. the orientation from sensor has updated again before
     * applying rotation to display) but the launching app has been transformed. So the record need
     * to be cleared and restored to stop using seamless rotation and rotated configuration.
     */
    private void clearFixedRotationLaunchingApp() {
        if (mFixedRotationLaunchingApp == null) {
            return;
        }
        // The display won't rotate (e.g. the orientation from sensor has updated again before
        // applying rotation to display), so clear it to stop using seamless rotation.
        mFixedRotationLaunchingApp.finishFixedRotationTransform();
        mFixedRotationLaunchingApp = null;
        return false;
    }

    private void startFixedRotationTransform(WindowToken token, int rotation) {
@@ -2858,6 +2887,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            pw.print("  mLastStatusBarVisibility=0x");
            pw.println(Integer.toHexString(mLastStatusBarVisibility));
        }
        if (mFixedRotationLaunchingApp != null) {
            pw.println("  mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp);
        }

        pw.println();
        mWallpaperController.dump(pw, "  ");
@@ -5184,7 +5216,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
        final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
        if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) {
            applyRotationAndClearFixedRotation(currRotation, overrideRotation);
            applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
        }
        mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
@@ -5200,7 +5232,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     * fixed rotation transform also needs to be cleared to make sure the rotated activity fits
     * the display naturally.
     */
    private void applyRotationAndClearFixedRotation(int oldRotation, int newRotation) {
    private void applyRotationAndFinishFixedRotation(int oldRotation, int newRotation) {
        if (mFixedRotationLaunchingApp == null) {
            applyRotation(oldRotation, newRotation);
            return;
@@ -5219,7 +5251,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            }
        });

        mFixedRotationLaunchingApp.clearFixedRotationTransform(
        mFixedRotationLaunchingApp.finishFixedRotationTransform(
                () -> applyRotation(oldRotation, newRotation));
        mFixedRotationLaunchingApp = null;
    }
@@ -5494,6 +5526,75 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        });
    }

    /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
    class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {

        /**
         * The animating activity which shows the recents task list. It is set between
         * {@link RecentsAnimationController#initialize} and
         * {@link RecentsAnimationController#cancelAnimation}.
         */
        private ActivityRecord mAnimatingRecents;

        /**
         * If the recents activity has a fixed orientation which is different from the current top
         * activity, it will be rotated before being shown so we avoid a screen rotation animation
         * when showing the Recents view.
         */
        void onStartRecentsAnimation(@NonNull ActivityRecord r) {
            mAnimatingRecents = r;
            rotateInDifferentOrientationIfNeeded(r);
            if (r.hasFixedRotationTransform()) {
                // Set the record so we can recognize it to continue to update display orientation
                // if the recents activity becomes the top later.
                setFixedRotationLaunchingApp(r, r.getWindowConfiguration().getRotation());
            }
        }

        /**
         * If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we
         * don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition.
         */
        void onFinishRecentsAnimation() {
            final ActivityRecord animatingRecents = mAnimatingRecents;
            mAnimatingRecents = null;
            if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp
                    && !animatingRecents.hasFixedRotationTransform()) {
                // The recents activity won't be the top, such as giving up the swipe up gesture
                // and return to the original top.
                mFixedRotationLaunchingApp = null;
            }
        }

        @Override
        public void onAppTransitionFinishedLocked(IBinder token) {
            final ActivityRecord r = getActivityRecord(token);
            // Ignore the animating recents so the fixed rotation transform won't be switched twice
            // by finishing the recents animation and moving it to top. That also avoids flickering
            // due to wait for previous activity to be paused if it supports PiP that ignores the
            // effect of resume-while-pausing.
            if (r == null || r == mAnimatingRecents) {
                return;
            }
            if (mFixedRotationLaunchingApp != null
                    && mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
                continueUpdateOrientationForDiffOrienLaunchingApp();
            } else {
                r.finishFixedRotationTransform();
            }
        }

        @Override
        public void onAppTransitionCancelledLocked(int transit) {
            continueUpdateOrientationForDiffOrienLaunchingApp();
        }

        @Override
        public void onAppTransitionTimeoutLocked() {
            continueUpdateOrientationForDiffOrienLaunchingApp();
        }
    }

    class RemoteInsetsControlTarget implements InsetsControlTarget {
        private final IDisplayWindowInsetsController mRemoteInsetsController;

+5 −6
Original line number Diff line number Diff line
@@ -411,10 +411,7 @@ public class RecentsAnimationController implements DeathRecipient {

        mService.mWindowPlacerLocked.performSurfacePlacement();

        // If the target activity has a fixed orientation which is different from the current top
        // activity, it will be rotated before being shown so we avoid a screen rotation
        // animation when showing the Recents view.
        mDisplayContent.rotateInDifferentOrientationIfNeeded(mTargetActivityRecord);
        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);

        // Notify that the animation has started
        if (mStatusBar != null) {
@@ -736,11 +733,13 @@ public class RecentsAnimationController implements DeathRecipient {
            if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
                mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
                        mTargetActivityRecord.token);
            }
            if (mTargetActivityRecord.hasFixedRotationTransform()) {
            } else {
                // The target activity will be moved to original position (non-top). Since it won't
                // affect display orientation, just finish the transform.
                mTargetActivityRecord.finishFixedRotationTransform();
            }
        }
        mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();

        // Notify that the animation has ended
        if (mStatusBar != null) {
+14 −22
Original line number Diff line number Diff line
@@ -119,7 +119,6 @@ class WindowToken extends WindowContainer<WindowState> {
     * rotated by the given rotated display info, frames and insets.
     */
    private static class FixedRotationTransformState {
        final WindowToken mOwner;
        final DisplayInfo mDisplayInfo;
        final DisplayFrames mDisplayFrames;
        final InsetsState mInsetsState;
@@ -133,10 +132,9 @@ class WindowToken extends WindowContainer<WindowState> {
        final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
        boolean mIsTransforming = true;

        FixedRotationTransformState(WindowToken owner, DisplayInfo rotatedDisplayInfo,
        FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
                DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState,
                Configuration rotatedConfig, int currentRotation) {
            mOwner = owner;
            mDisplayInfo = rotatedDisplayInfo;
            mDisplayFrames = rotatedDisplayFrames;
            mInsetsState = rotatedInsetsState;
@@ -482,6 +480,14 @@ class WindowToken extends WindowContainer<WindowState> {
        return mFixedRotationTransformState != null;
    }

    /** Returns {@code true} if the given token shares the same transform. */
    boolean hasFixedRotationTransform(WindowToken token) {
        if (mFixedRotationTransformState == null || token == null) {
            return false;
        }
        return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState;
    }

    boolean isFinishingFixedRotationTransform() {
        return mFixedRotationTransformState != null
                && !mFixedRotationTransformState.mIsTransforming;
@@ -520,15 +526,14 @@ class WindowToken extends WindowContainer<WindowState> {
        final InsetsState insetsState = new InsetsState();
        mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState,
                mDisplayContent.getConfiguration().uiMode);
        mFixedRotationTransformState = new FixedRotationTransformState(this, info, displayFrames,
        mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
                insetsState, new Configuration(config), mDisplayContent.getRotation());
        onConfigurationChanged(getParent().getConfiguration());
    }

    /**
     * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
     * one. This takes the same effect as {@link #applyFixedRotationTransform}, but the linked state
     * can only be cleared by the state owner.
     * one. This takes the same effect as {@link #applyFixedRotationTransform}.
     */
    void linkFixedRotationTransform(WindowToken other) {
        if (mFixedRotationTransformState != null) {
@@ -543,28 +548,15 @@ class WindowToken extends WindowContainer<WindowState> {
        onConfigurationChanged(getParent().getConfiguration());
    }

    /**
     * Finishes the transform and continue updating the orientation change of display. Only the
     * state owner can finish the transform state.
     */
    void finishFixedRotationTransform() {
        if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) {
            return;
        }
        final boolean changed =
                mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this);
        // If it is not the launching app or the display is not rotated, make sure the transform is
        // cleared and the configuration is restored from parent.
        if (!changed) {
            clearFixedRotationTransform(null /* applyDisplayRotation */);
        }
        finishFixedRotationTransform(null /* applyDisplayRotation */);
    }

    /**
     * Clears the transform and apply display rotation if the action is given. If the display will
     * Finishes the transform and apply display rotation if the action is given. If the display will
     * not rotate, the transformed containers are restored to their original states.
     */
    void clearFixedRotationTransform(Runnable applyDisplayRotation) {
    void finishFixedRotationTransform(Runnable applyDisplayRotation) {
        final FixedRotationTransformState state = mFixedRotationTransformState;
        if (state == null) {
            return;
+13 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;

import android.app.ActivityOptions;
@@ -1402,6 +1403,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
        final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
        mActivity.finishFixedRotationTransform();
        final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
        assertNotNull(rotationAnim);
        rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);

        // Because the display doesn't rotate, the rotated activity needs to cancel the fixed
@@ -1409,8 +1411,18 @@ public class ActivityRecordTests extends ActivityTestsBase {
        verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
        assertTrue(mActivity.isFreezingScreen());
        assertFalse(displayRotation.isRotatingSeamlessly());
        assertNotNull(rotationAnim);
        assertTrue(rotationAnim.isRotating());

        // Simulate the remote rotation has completed and the configuration doesn't change, then
        // the rotated activity should also be restored by clearing the transform.
        displayRotation.updateRotationUnchecked(true /* forceUpdate */);
        doReturn(false).when(displayRotation).isWaitingForRemoteRotation();
        clearInvocations(mActivity);
        display.mFixedRotationLaunchingApp = mActivity;
        display.sendNewConfiguration();

        assertNull(display.mFixedRotationLaunchingApp);
        assertFalse(mActivity.hasFixedRotationTransform());
    }

    @Test
+24 −8
Original line number Diff line number Diff line
@@ -790,9 +790,7 @@ public class DisplayContentTests extends WindowTestsBase {
        final DisplayContent dc = createNewDisplay();
        dc.getDisplayRotation().setFixedToUserRotation(
                IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                ? SCREEN_ORIENTATION_PORTRAIT
                : SCREEN_ORIENTATION_LANDSCAPE;
        final int newOrientation = getRotatedOrientation(dc);

        final ActivityStack stack =
                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
@@ -812,9 +810,7 @@ public class DisplayContentTests extends WindowTestsBase {
        final DisplayContent dc = createNewDisplay();
        dc.getDisplayRotation().setFixedToUserRotation(
                IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                ? SCREEN_ORIENTATION_PORTRAIT
                : SCREEN_ORIENTATION_LANDSCAPE;
        final int newOrientation = getRotatedOrientation(dc);

        final ActivityStack stack =
                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
@@ -1083,7 +1079,8 @@ public class DisplayContentTests extends WindowTestsBase {
        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
                false /* alwaysKeepCurrent */);
        mDisplayContent.mOpeningApps.add(app);
        app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
        final int newOrientation = getRotatedOrientation(mDisplayContent);
        app.setRequestedOrientation(newOrientation);

        assertTrue(app.isFixedRotationTransforming());
        assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly(
@@ -1124,12 +1121,25 @@ public class DisplayContentTests extends WindowTestsBase {
        mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */);
        verify(t, never()).setPosition(any(), eq(0), eq(0));

        // Launch another activity before the transition is finished.
        final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
                .setDisplay(mDisplayContent).build().getTopMostActivity();
        mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
                false /* alwaysKeepCurrent */);
        mDisplayContent.mOpeningApps.add(app2);
        app2.setRequestedOrientation(newOrientation);

        // The activity should share the same transform state as the existing one.
        assertTrue(app.hasFixedRotationTransform(app2));

        // The display should be rotated after the launch is finished.
        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);

        // The animation in old rotation should be cancelled.
        assertFalse(closingApp.isAnimating());
        // The display should be rotated after the launch is finished.
        // The fixed rotation should be cleared and the new rotation is applied to display.
        assertFalse(app.hasFixedRotationTransform());
        assertFalse(app2.hasFixedRotationTransform());
        assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
    }

@@ -1292,6 +1302,12 @@ public class DisplayContentTests extends WindowTestsBase {
        assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
    }

    private static int getRotatedOrientation(DisplayContent dc) {
        return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                ? SCREEN_ORIENTATION_PORTRAIT
                : SCREEN_ORIENTATION_LANDSCAPE;
    }

    private static List<WindowState> reverseList(List<WindowState> list) {
        final ArrayList<WindowState> result = new ArrayList<>(list);
        Collections.reverse(result);
Loading