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

Commit d16620e8 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Apply rotation animation to restore rotated activity

Consider the steps with fixed rotation enabled:
1. Sensor reports landscape and the foreground is a fixed
   portrait activity, so the display is portrait.
2. Launch a rotatable activity so the activity is created
   in landscape. Before the remote rotation completes, the
   display hasn't applied the rotation.
3. Sensor reports portrait and then the remote rotation is
   done. Display is still portrait but the rotatable activity
   has shown as rotated.

To avoid flickering when updating the configuration of the
activity from landscape to portrait, a rotation animation
with customized original rotation is still applied to cover
the change.

Fixes: 151597653
Test: ActivityRecordTests#testActivityOnCancelFixedRotationTransform

Change-Id: Ie897885782aaa1411113bb7fdbac6c4f33fdc0ef
parent 13d870c8
Loading
Loading
Loading
Loading
+51 −14
Original line number Diff line number Diff line
@@ -5112,28 +5112,50 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }

    void startFreezingScreen() {
        startFreezingScreen(ROTATION_UNDEFINED /* overrideOriginalDisplayRotation */);
    }

    void startFreezingScreen(int overrideOriginalDisplayRotation) {
        ProtoLog.i(WM_DEBUG_ORIENTATION,
                "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
                appToken, isVisible(), mFreezingScreen, mVisibleRequested,
                new RuntimeException().fillInStackTrace());
        if (mVisibleRequested) {
        if (!mVisibleRequested) {
            return;
        }

        // If the override is given, the rotation of display doesn't change but we still want to
        // cover the activity whose configuration is changing by freezing the display and running
        // the rotation animation.
        final boolean forceRotation = overrideOriginalDisplayRotation != ROTATION_UNDEFINED;
        if (!mFreezingScreen) {
            mFreezingScreen = true;
            mWmService.registerAppFreezeListener(this);
            mWmService.mAppsFreezingScreen++;
            if (mWmService.mAppsFreezingScreen == 1) {
                    mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
                if (forceRotation) {
                    // Make sure normal rotation animation will be applied.
                    mDisplayContent.getDisplayRotation().cancelSeamlessRotation();
                }
                mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */,
                        mDisplayContent, overrideOriginalDisplayRotation);
                mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
                mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
            }
        }
        if (forceRotation) {
            // The rotation of the real display won't change, so in order to unfreeze the screen
            // via {@link #checkAppWindowsReadyToShow}, the windows have to be able to call
            // {@link WindowState#reportResized} (it is skipped if the window is freezing) to update
            // the drawn state.
            return;
        }
        final int count = mChildren.size();
        for (int i = 0; i < count; i++) {
            final WindowState w = mChildren.get(i);
            w.onStartFreezingScreen();
        }
    }
    }

    boolean isFreezingScreen() {
        return mFreezingScreen;
@@ -6145,6 +6167,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
    }

    @Override
    void onCancelFixedRotationTransform(int originalDisplayRotation) {
        if (this != mDisplayContent.getLastOrientationSource()
                || getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED) {
            // Only need to handle the activity that should be rotated with display.
            return;
        }

        // Perform rotation animation according to the rotation of this activity.
        startFreezingScreen(originalDisplayRotation);
        // This activity may relaunch or perform configuration change so once it has reported drawn,
        // the screen can be unfrozen.
        ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
    }

    void setRequestedOrientation(int requestedOrientation) {
        setOrientation(requestedOrientation, mayFreezeScreenLocked());
        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+5 −1
Original line number Diff line number Diff line
@@ -494,6 +494,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     * The launching activity which is using fixed rotation transformation.
     *
     * @see #handleTopActivityLaunchingInDifferentOrientation
     * @see DisplayRotation#shouldRotateSeamlessly
     */
    ActivityRecord mFixedRotationLaunchingApp;

@@ -1237,7 +1238,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo

        if (configChanged) {
            mWaitingForConfig = true;
            mWmService.startFreezingDisplayLocked(0 /* exitAnim */, 0 /* enterAnim */, this);
            mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
            sendNewConfiguration();
        }

@@ -1475,6 +1476,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            sendNewConfiguration();
            return true;
        }
        // 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 = null;
        return false;
    }

+22 −1
Original line number Diff line number Diff line
@@ -537,8 +537,29 @@ public class DisplayRotation {
    }

    void prepareNormalRotationAnimation() {
        cancelSeamlessRotation();
        final RotationAnimationPair anim = selectRotationAnimation();
        mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
        mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
    }

    /**
     * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
     * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
     * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
     * and it doesn't choose seamless rotation.
     */
    void cancelSeamlessRotation() {
        if (!mRotatingSeamlessly) {
            return;
        }
        mDisplayContent.forAllWindows(w -> {
            if (w.mSeamlesslyRotated) {
                w.finishSeamlessRotation(false /* timeout */);
                w.mSeamlesslyRotated = false;
            }
        }, true /* traverseTopToBottom */);
        mSeamlessRotationCount = 0;
        mRotatingSeamlessly = false;
    }

    private void prepareSeamlessRotation() {
+23 −17
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import android.graphics.Rect;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
@@ -117,8 +116,9 @@ class ScreenRotationAnimation {
    private BlackFrame mEnteringBlackFrame;
    private int mWidth, mHeight;

    private int mOriginalRotation;
    private int mOriginalWidth, mOriginalHeight;
    private final int mOriginalRotation;
    private final int mOriginalWidth;
    private final int mOriginalHeight;
    private int mCurRotation;

    private Rect mOriginalDisplayRect = new Rect();
@@ -140,20 +140,18 @@ class ScreenRotationAnimation {
    /** Intensity of light/whiteness of the layout after rotation occurs. */
    private float mEndLuma;

    public ScreenRotationAnimation(Context context, DisplayContent displayContent,
            boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
        mService = service;
        mContext = context;
    ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
        mService = displayContent.mWmService;
        mContext = mService.mContext;
        mDisplayContent = displayContent;
        displayContent.getBounds(mOriginalDisplayRect);

        // Screenshot does NOT include rotation!
        final Display display = displayContent.getDisplay();
        int originalRotation = display.getRotation();
        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
        final int realOriginalRotation = displayInfo.rotation;
        final int originalWidth;
        final int originalHeight;
        DisplayInfo displayInfo = displayContent.getDisplayInfo();
        if (fixedToUserRotation) {
        if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
            // Emulated orientation.
            mForceDefaultOrientation = true;
            originalWidth = displayContent.mBaseDisplayWidth;
@@ -163,8 +161,8 @@ class ScreenRotationAnimation {
            originalWidth = displayInfo.logicalWidth;
            originalHeight = displayInfo.logicalHeight;
        }
        if (originalRotation == Surface.ROTATION_90
                || originalRotation == Surface.ROTATION_270) {
        if (realOriginalRotation == Surface.ROTATION_90
                || realOriginalRotation == Surface.ROTATION_270) {
            mWidth = originalHeight;
            mHeight = originalWidth;
        } else {
@@ -173,10 +171,18 @@ class ScreenRotationAnimation {
        }

        mOriginalRotation = originalRotation;
        mOriginalWidth = originalWidth;
        mOriginalHeight = originalHeight;
        // If the delta is not zero, the rotation of display may not change, but we still want to
        // apply rotation animation because there should be a top app shown as rotated. So the
        // specified original rotation customizes the direction of animation to have better look
        // when restoring the rotated app to the same rotation as current display.
        final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
        final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
        mOriginalWidth = flipped ? originalHeight : originalWidth;
        mOriginalHeight = flipped ? originalWidth : originalHeight;
        mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();

        // Check whether the current screen contains any secure content.
        final boolean isSecure = displayContent.hasSecureWindowOnScreen();
        final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
        try {
            mBackColorSurface = displayContent.makeChildSurface(null)
@@ -202,7 +208,7 @@ class ScreenRotationAnimation {
            t2.apply(true /* sync */);

            // Capture a screenshot into the surface we just created.
            final int displayId = display.getDisplayId();
            final int displayId = displayContent.getDisplayId();
            final Surface surface = mService.mSurfaceFactory.get();
            surface.copyFrom(mScreenshotLayer);
            SurfaceControl.ScreenshotGraphicBuffer gb =
@@ -242,7 +248,7 @@ class ScreenRotationAnimation {

        ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                    "  FREEZE %s: CREATE", mScreenshotLayer);
        setRotation(t, originalRotation);
        setRotation(t, realOriginalRotation);
        t.apply();
    }

+16 −13
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
@@ -2941,7 +2942,7 @@ public class WindowManagerService extends IWindowManager.Stub
                mClientFreezingScreen = true;
                final long origId = Binder.clearCallingIdentity();
                try {
                    startFreezingDisplayLocked(exitAnim, enterAnim);
                    startFreezingDisplay(exitAnim, enterAnim);
                    mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
                    mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
                } finally {
@@ -5479,13 +5480,17 @@ public class WindowManagerService extends IWindowManager.Stub
        return changed;
    }

    void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
        startFreezingDisplayLocked(exitAnim, enterAnim,
                getDefaultDisplayContentLocked());
    void startFreezingDisplay(int exitAnim, int enterAnim) {
        startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());
    }

    void startFreezingDisplayLocked(int exitAnim, int enterAnim,
            DisplayContent displayContent) {
    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {
        startFreezingDisplay(exitAnim, enterAnim, displayContent,
                ROTATION_UNDEFINED /* overrideOriginalRotation */);
    }

    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
            int overrideOriginalRotation) {
        if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
            return;
        }
@@ -5529,14 +5534,12 @@ public class WindowManagerService extends IWindowManager.Stub
            screenRotationAnimation.kill();
        }

        // Check whether the current screen contains any secure content.
        boolean isSecure = displayContent.hasSecureWindowOnScreen();

        displayContent.updateDisplayInfo();
        screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
                this);
        displayContent.setRotationAnimation(screenRotationAnimation);
        final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
                ? overrideOriginalRotation
                : displayContent.getDisplayInfo().rotation;
        displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
                originalRotation));
    }

    void stopFreezingDisplayLocked() {
Loading