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

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

Support rotation on secondary displays (2/N)

Move partial operations of rotation/orientation from
PhoneWindowManager to DisplayRotation.

Bug: 111361251
Test: go/wm-smoke
Test: atest FrameworksServicesTests:PhoneWindowManagerLayoutTest
Test: atest FrameworksServicesTests:PhoneWindowManagerInsetsTest
Change-Id: I7507e25f14e7fd1733ffd2a71789673d6a7ee17d
parent 11eedb63
Loading
Loading
Loading
Loading
+122 −231

File changed.

Preview size limit exceeded, changes collapsed.

+33 −27
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ import android.view.animation.Animation;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;

@@ -177,7 +178,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
    /**
     * Called when the resource overlays change.
     */
    default void onOverlayChangedLw() {}
    default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {}

    /**
     * Interface to the Window Manager state associated with a particular
@@ -638,6 +639,27 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
         * The keyguard showing state has changed
         */
        void onKeyguardShowingAndNotOccludedChanged();

        DisplayContentInfo getDefaultDisplayContentInfo();
    }

    /**
     * Provides the rotation of a device.
     *
     * @see com.android.server.policy.WindowOrientationListener
     */
    public interface RotationSource {
        int getProposedRotation();

        void setCurrentRotation(int rotation);
    }

    /**
     * Interface to get public information of a display content.
     */
    public interface DisplayContentInfo {
        DisplayRotation getDisplayRotation();
        Display getDisplay();
    }

    /** Window has been added to the screen. */
@@ -685,7 +707,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     * Called by window manager once it has the initial, default native
     * display dimensions.
     */
    public void setInitialDisplaySize(Display display, int width, int height, int density);
    public void setInitialDisplaySize(DisplayRotation displayRotation, int width, int height,
            int density);

    /**
     * Check permissions when adding a window.
@@ -1421,31 +1444,15 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     *                       rotation lock and other factors are ignored.
     * @return The surface rotation to use.
     */
    public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
            int lastRotation, boolean defaultDisplay);
    public int rotationForOrientationLw(DisplayRotation displayRotation,
            @ActivityInfo.ScreenOrientation int orientation, int lastRotation);

    /**
     * Given an orientation constant and a rotation, returns true if the rotation
     * has compatible metrics to the requested orientation.  For example, if
     * the application requested landscape and got seascape, then the rotation
     * has compatible metrics; if the application requested portrait and got landscape,
     * then the rotation has incompatible metrics; if the application did not specify
     * a preference, then anything goes.
     *
     * @param orientation An orientation constant, such as
     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
     * @param rotation The rotation to check.
     * @return True if the rotation is compatible with the requested orientation.
     */
    public boolean rotationHasCompatibleMetricsLw(@ActivityInfo.ScreenOrientation int orientation,
            int rotation);
    public void updateOrientationListener();

    /**
     * Called by the window manager when the rotation changes.
     *
     * @param rotation The new rotation.
     * Get rotation source for the given display id.
     */
    public void setRotationLw(int rotation);
    public RotationSource getRotationSource(int displayId);

    /**
     * Called when the system is mostly done booting to set whether
@@ -1487,8 +1494,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     */
    public void enableScreenAfterBoot();

    public void setCurrentOrientationLw(@ActivityInfo.ScreenOrientation int newOrientation);

    /**
     * Call from application to perform haptic feedback on its window.
     */
@@ -1702,9 +1707,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
    /**
     * Called when the configuration has changed, and it's safe to load new values from resources.
     */
    public void onConfigurationChanged();
    public void onConfigurationChanged(DisplayContentInfo displayContentInfo);

    public boolean shouldRotateSeamlessly(int oldRotation, int newRotation);
    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation,
            int oldRotation, int newRotation);

    /**
     * Called when System UI has been started.
+43 −14
Original line number Diff line number Diff line
@@ -173,7 +173,8 @@ import java.util.function.Predicate;
 * IMPORTANT: No method from this class should ever be used without holding
 * WindowManagerService.mWindowMap.
 */
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
        implements WindowManagerPolicy.DisplayContentInfo {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;

    /** Unique identifier of this stack. */
@@ -234,6 +235,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final Display mDisplay;
    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
    final DisplayRotation mDisplayRotation;
    DisplayFrames mDisplayFrames;

    /**
@@ -762,6 +764,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
                calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
        initializeDisplayBaseInfo();
        mDisplayRotation = new DisplayRotation(this, mService.mPolicy, mService.mContext);
        mDividerControllerLocked = new DockedStackDividerController(service, this);
        mPinnedStackControllerLocked = new PinnedStackController(service, this);

@@ -905,7 +908,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        appToken.onRemovedFromDisplay();
    }

    Display getDisplay() {
    @Override
    public Display getDisplay() {
        return mDisplay;
    }

@@ -917,6 +921,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        return mDisplayMetrics;
    }

    @Override
    public DisplayRotation getDisplayRotation() {
        return mDisplayRotation;
    }

    int getRotation() {
        return mRotation;
    }
@@ -924,6 +933,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    @VisibleForTesting
    void setRotation(int newRotation) {
        mRotation = newRotation;
        mDisplayRotation.setRotation(newRotation);
    }

    int getLastOrientation() {
@@ -976,6 +986,28 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        }
    }

    /**
     * If this is true we have updated our desired orientation, but not yet changed the real
     * orientation our applied our screen rotation animation. For example, because a previous
     * screen rotation was in progress.
     *
     * @return {@code true} if the there is an ongoing rotation change.
     */
    boolean rotationNeedsUpdate() {
        final int lastOrientation = getLastOrientation();
        final int oldRotation = getRotation();
        final boolean oldAltOrientation = getAltOrientation();

        final int rotation = mService.mPolicy.rotationForOrientationLw(
                mDisplayRotation, lastOrientation, oldRotation);
        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
                lastOrientation, rotation);
        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
            return false;
        }
        return true;
    }

    /**
     * Update rotation of the display.
     *
@@ -1034,13 +1066,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        final int oldRotation = mRotation;
        final int lastOrientation = mLastOrientation;
        final boolean oldAltOrientation = mAltOrientation;
        final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
                isDefaultDisplay);
        final int rotation = mService.mPolicy.rotationForOrientationLw(mDisplayRotation,
                lastOrientation, oldRotation);
        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
                + mDisplayId + " based on lastOrientation=" + lastOrientation
                + " and oldRotation=" + oldRotation);
        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
                rotation);
        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation,
                oldRotation, rotation);

        if (mayRotateSeamlessly) {
            final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
@@ -1071,7 +1103,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        //       an orientation that has different metrics than it expected.
        //       eg. Portrait instead of Landscape.

        final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
                lastOrientation, rotation);

        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
@@ -1093,11 +1125,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            mService.mWaitingForConfig = true;
        }

        mRotation = rotation;
        setRotation(rotation);
        mAltOrientation = altOrientation;
        if (isDefaultDisplay) {
            mService.mPolicy.setRotationLw(rotation);
        }

        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
        mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
@@ -1191,9 +1220,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    }

    void configureDisplayPolicy() {
        mService.mPolicy.setInitialDisplaySize(getDisplay(),
                mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);

        mDisplayRotation.configure();
        mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
    }
@@ -2367,6 +2394,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        pw.println();
        mDisplayFrames.dump(prefix, pw);
        pw.println();
        mDisplayRotation.dump(prefix, pw);
        pw.println();
        mInputMonitor.dump(pw, "  ");
    }

+260 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.view.Surface;

import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.RotationSource;

import java.io.PrintWriter;

/**
 * Defines the mapping between orientation and rotation of a display.
 */
public class DisplayRotation {
    private final DisplayContent mDisplayContent;
    private final WindowManagerPolicy mPolicy;
    private final Context mContext;
    private RotationSource mRotationSource;

    int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
    int mLandscapeRotation;  // default landscape
    int mSeascapeRotation;   // "other" landscape, 180 degrees from mLandscapeRotation
    int mPortraitRotation;   // default portrait
    int mUpsideDownRotation; // "other" portrait

    DisplayRotation(DisplayContent displayContent, WindowManagerPolicy policy, Context context) {
        mDisplayContent = displayContent;
        mPolicy = policy;
        mContext = context;
    }

    void configure() {
        mRotationSource = mPolicy.getRotationSource(mDisplayContent.getDisplayId());

        final int width = mDisplayContent.mBaseDisplayWidth;
        final int height = mDisplayContent.mBaseDisplayHeight;
        final Resources res = mContext.getResources();
        if (width > height) {
            mLandscapeRotation = Surface.ROTATION_0;
            mSeascapeRotation = Surface.ROTATION_180;
            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
                mPortraitRotation = Surface.ROTATION_90;
                mUpsideDownRotation = Surface.ROTATION_270;
            } else {
                mPortraitRotation = Surface.ROTATION_270;
                mUpsideDownRotation = Surface.ROTATION_90;
            }
        } else {
            mPortraitRotation = Surface.ROTATION_0;
            mUpsideDownRotation = Surface.ROTATION_180;
            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
                mLandscapeRotation = Surface.ROTATION_270;
                mSeascapeRotation = Surface.ROTATION_90;
            } else {
                mLandscapeRotation = Surface.ROTATION_90;
                mSeascapeRotation = Surface.ROTATION_270;
            }
        }

        mPolicy.setInitialDisplaySize(this, width, height, mDisplayContent.mBaseDisplayDensity);
    }

    public int getLandscapeRotation() {
        return mLandscapeRotation;
    }

    public int getSeascapeRotation() {
        return mSeascapeRotation;
    }

    public int getPortraitRotation() {
        return mPortraitRotation;
    }

    public int getUpsideDownRotation() {
        return mUpsideDownRotation;
    }

    public int getCurrentAppOrientation() {
        return mCurrentAppOrientation;
    }

    public int getSensorRotation() {
        return mRotationSource != null ? mRotationSource.getProposedRotation() : -1;
    }

    public boolean isDefaultDisplay() {
        return mDisplayContent.isDefaultDisplay;
    }

    void setRotation(int rotation) {
        if (mRotationSource != null) {
            mRotationSource.setCurrentRotation(rotation);
        }
    }

    void setCurrentOrientation(int newOrientation) {
        if (newOrientation != mCurrentAppOrientation) {
            mCurrentAppOrientation = newOrientation;
            // TODO(multi-display): Separate orientation listeners.
            if (mDisplayContent.isDefaultDisplay) {
                mPolicy.updateOrientationListener();
            }
        }
    }

    public int rotationForOrientation(int orientation, int lastRotation, int preferredRotation) {
        switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                // Return portrait unless overridden.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                return mPortraitRotation;

            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                // Return landscape unless overridden.
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                return mLandscapeRotation;

            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
                // Return reverse portrait unless overridden.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                return mUpsideDownRotation;

            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
                // Return seascape unless overridden.
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                return mSeascapeRotation;

            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
                // Return either landscape rotation.
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                if (isLandscapeOrSeascape(lastRotation)) {
                    return lastRotation;
                }
                return mLandscapeRotation;

            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
                // Return either portrait rotation.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                if (isAnyPortrait(lastRotation)) {
                    return lastRotation;
                }
                return mPortraitRotation;

            default:
                // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
                // just return the preferred orientation we already calculated.
                if (preferredRotation >= 0) {
                    return preferredRotation;
                }
                return Surface.ROTATION_0;
        }
    }

    /**
     * Given an orientation constant and a rotation, returns true if the rotation
     * has compatible metrics to the requested orientation.  For example, if
     * the application requested landscape and got seascape, then the rotation
     * has compatible metrics; if the application requested portrait and got landscape,
     * then the rotation has incompatible metrics; if the application did not specify
     * a preference, then anything goes.
     *
     * @param orientation An orientation constant, such as
     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
     * @param rotation The rotation to check.
     * @return True if the rotation is compatible with the requested orientation.
     */
    boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
        switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
                return isAnyPortrait(rotation);

            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
                return isLandscapeOrSeascape(rotation);

            default:
                return true;
        }
    }

    public boolean isValidRotationChoice(final int preferredRotation) {
        // Determine if the given app orientation is compatible with the provided rotation choice.
        switch (mCurrentAppOrientation) {
            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
                // Works with any of the 4 rotations.
                return preferredRotation >= 0;

            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
                // It's possible for the user pref to be set at 180 because of FULL_USER. This would
                // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
                // but never to go to 180.
                return preferredRotation == mPortraitRotation;

            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
                // Works landscape or seascape.
                return isLandscapeOrSeascape(preferredRotation);

            case ActivityInfo.SCREEN_ORIENTATION_USER:
            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
                // Works with any rotation except upside down.
                return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
        }

        return false;
    }

    private boolean isLandscapeOrSeascape(int rotation) {
        return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
    }

    private boolean isAnyPortrait(int rotation) {
        return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
    }

    void dump(String prefix, PrintWriter pw) {
        pw.println(prefix + "DisplayRotation");
        pw.println(prefix + "  mCurrentAppOrientation="
                + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
        pw.print(prefix + "  mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
        pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
        pw.print(prefix + "  mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
        pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -134,6 +134,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
    // transaction from the global transaction.
    private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();

    private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer =
            mService.mPolicy::onConfigurationChanged;

    private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
        if (w.mHasSurface) {
            try {
@@ -379,7 +382,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
        prepareFreezingTaskBounds();
        super.onConfigurationChanged(newParentConfig);

        mService.mPolicy.onConfigurationChanged();
        forAllDisplays(mDisplayContentConfigChangesConsumer);
    }

    /**
Loading