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

Commit 90c9005e authored by Garfield Tan's avatar Garfield Tan
Browse files

Persist user rotations of external displays.

It also introduces a command line tool to rotate external displays for
testing purposes.

Instead of changing signatures of freezeRotation(), thawRotation() and
isRotationFrozen(), introduce 3 new methods to IWindowManager interface.
The old methods are being used pervasively.

Also resolved some style issues in DisplaySettings class.

Bug: 113252523
Bug: 111361251
Test: Rotation locks via settings still work. User rotation mode and
user rotation values for external devices are persisted as expected in
storage. DisplaySettingsTests passes.

Change-Id: I1746bea98a588f0bbd30c9f0617a7a23dc6e4209
parent 9ad6ab59
Loading
Loading
Loading
Loading
+33 −8
Original line number Diff line number Diff line
@@ -226,25 +226,50 @@ interface IWindowManager
    int getPreferredOptionsPanelGravity(int displayId);

    /**
     * Lock the device orientation to the specified rotation, or to the
     * current rotation if -1.  Sensor input will be ignored until
     * thawRotation() is called.
     * @hide
     * Equivalent to calling {@link #freezeDisplayRotation(int, int)} with {@link
     * android.view.Display#DEFAULT_DISPLAY} and given rotation.
     */
    void freezeRotation(int rotation);

    /**
     * Release the orientation lock imposed by freezeRotation().
     * @hide
     * Equivalent to calling {@link #thawDisplayRotation(int)} with {@link
     * android.view.Display#DEFAULT_DISPLAY}.
     */
    void thawRotation();

    /**
     * Gets whether the rotation is frozen.
     * Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link
     * android.view.Display#DEFAULT_DISPLAY}.
     */
    boolean isRotationFrozen();

    /**
     * Lock the display orientation to the specified rotation, or to the current
     * rotation if -1. Sensor input will be ignored until thawRotation() is called.
     *
     * @param displayId the ID of display which rotation should be frozen.
     * @param rotation one of {@link android.view.Surface#ROTATION_0},
     *        {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
     *        {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation.
     * @hide
     */
    void freezeDisplayRotation(int displayId, int rotation);

    /**
     * Release the orientation lock imposed by freezeRotation() on the display.
     *
     * @param displayId the ID of display which rotation should be thawed.
     * @hide
     */
    void thawDisplayRotation(int displayId);

    /**
     * Gets whether the rotation is frozen on the display.
     *
     * @param displayId the ID of display which frozen is needed.
     * @return Whether the rotation is frozen.
     */
    boolean isRotationFrozen();
    boolean isDisplayRotationFrozen(int displayId);

    /**
     * Screenshot the current wallpaper layer, including the whole screen.
+0 −31
Original line number Diff line number Diff line
@@ -6859,37 +6859,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        PhoneWindow.sendCloseSystemWindows(mContext, reason);
    }

    @Override
    public int getUserRotationMode() {
        return Settings.System.getIntForUser(mContext.getContentResolver(),
                Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
                        WindowManagerPolicy.USER_ROTATION_FREE :
                                WindowManagerPolicy.USER_ROTATION_LOCKED;
    }

    // User rotation: to be used when all else fails in assigning an orientation to the device
    @Override
    public void setUserRotationMode(int mode, int rot) {
        ContentResolver res = mContext.getContentResolver();

        // mUserRotationMode and mUserRotation will be assigned by the content observer
        if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
            Settings.System.putIntForUser(res,
                    Settings.System.USER_ROTATION,
                    rot,
                    UserHandle.USER_CURRENT);
            Settings.System.putIntForUser(res,
                    Settings.System.ACCELEROMETER_ROTATION,
                    0,
                    UserHandle.USER_CURRENT);
        } else {
            Settings.System.putIntForUser(res,
                    Settings.System.ACCELEROMETER_ROTATION,
                    1,
                    UserHandle.USER_CURRENT);
        }
    }

    @Override
    public void setSafeMode(boolean safeMode) {
        mSafeMode = safeMode;
+0 −21
Original line number Diff line number Diff line
@@ -82,7 +82,6 @@ import android.view.IApplicationToken;
import android.view.IWindowManager;
import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
@@ -1482,26 +1481,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     */
    public void keepScreenOnStoppedLw();

    /**
     * Gets the current user rotation mode.
     *
     * @return The rotation mode.
     *
     * @see #USER_ROTATION_LOCKED
     * @see #USER_ROTATION_FREE
     */
    @UserRotationMode
    public int getUserRotationMode();

    /**
     * Inform the policy that the user has chosen a preferred orientation ("rotation lock").
     *
     * @param mode One of {@link #USER_ROTATION_LOCKED} or {@link #USER_ROTATION_FREE}.
     * @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
     *                 {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
     */
    public void setUserRotationMode(@UserRotationMode int mode, @Surface.Rotation int rotation);

    /**
     * Called when a new system UI visibility is being reported, allowing
     * the policy to adjust what is actually reported.
+68 −5
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ public class DisplayRotation {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;

    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    private final DisplayPolicy mDisplayPolicy;
    private final Context mContext;
    private final Object mLock;
@@ -106,6 +107,7 @@ public class DisplayRotation {
    DisplayRotation(WindowManagerService service, DisplayContent displayContent,
            DisplayPolicy displayPolicy, Context context, Object lock) {
        mService = service;
        mDisplayContent = displayContent;
        mDisplayPolicy = displayPolicy;
        mContext = context;
        mLock = lock;
@@ -225,6 +227,70 @@ public class DisplayRotation {
        }
    }

    void restoreUserRotation(int userRotationMode, int userRotation) {
        if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
                && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
            Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
                    + " for " + mDisplayContent);
            userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
        }
        if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
            Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
                    + " for " + mDisplayContent);
            userRotation = Surface.ROTATION_0;
        }
        mUserRotationMode = userRotationMode;
        mUserRotation = userRotation;
    }

    private void setUserRotation(int userRotationMode, int userRotation) {
        if (isDefaultDisplay) {
            // We'll be notified via settings listener, so we don't need to update internal values.
            final ContentResolver res = mContext.getContentResolver();
            final int accelerometerRotation =
                    userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
            Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
                    accelerometerRotation, UserHandle.USER_CURRENT);
            Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
                    UserHandle.USER_CURRENT);
            return;
        }

        boolean changed = false;
        if (mUserRotationMode != userRotationMode) {
            mUserRotationMode = userRotationMode;
            changed = true;
        }
        if (mUserRotation != userRotation) {
            mUserRotation = userRotation;
            changed = true;
        }
        mService.mDisplaySettings.setUserRotation(mDisplayContent, userRotationMode, userRotation);
        if (changed) {
            mService.updateRotation(true /* alwaysSendConfiguration */,
                    false /* forceRelayout */);
            mService.mDisplaySettings.writeSettingsLocked();
        }
    }

    void freezeRotation(int rotation) {
        rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
        setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
    }

    void thawRotation() {
        setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
    }

    boolean isRotationFrozen() {
        if (!isDefaultDisplay) {
            return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
        }

        return Settings.System.getIntForUser(mContext.getContentResolver(),
                Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
    }

    /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
    boolean isDefaultOrientationForced() {
        return mForceDefaultOrientation;
@@ -381,9 +447,6 @@ public class DisplayRotation {
     * @param orientation An orientation constant, such as
     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
     * @param lastRotation The most recently used rotation.
     * @param defaultDisplay Flag indicating whether the rotation is computed for the default
     *                       display. Currently for all non-default displays sensors, docking mode,
     *                       rotation lock and other factors are ignored.
     * @return The surface rotation to use.
     */
    int rotationForOrientation(int orientation, int lastRotation) {
@@ -418,8 +481,8 @@ public class DisplayRotation {
        final int preferredRotation;
        if (!isDefaultDisplay) {
            // For secondary displays we ignore things like displays sensors, docking mode and
            // rotation lock, and always prefer a default rotation.
            preferredRotation = Surface.ROTATION_0;
            // rotation lock, and always prefer user rotation.
            preferredRotation = mUserRotation;
        } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
            // Ignore sensor when lid switch is open and rotation is forced.
            preferredRotation = mLidOpenRotation;
+113 −50
Original line number Diff line number Diff line
@@ -20,20 +20,23 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Environment;
import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.policy.WindowManagerPolicy;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileInputStream;
@@ -43,10 +46,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

/**
 * Current persistent settings about a display
 */
@@ -58,15 +57,25 @@ class DisplaySettings {
    private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();

    private static class Entry {
        private final String name;
        private int overscanLeft;
        private int overscanTop;
        private int overscanRight;
        private int overscanBottom;
        private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
        private final String mName;
        private int mOverscanLeft;
        private int mOverscanTop;
        private int mOverscanRight;
        private int mOverscanBottom;
        private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
        private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
        private int mUserRotation = Surface.ROTATION_0;

        private Entry(String _name) {
            name = _name;
            mName = _name;
        }

        private boolean isEmpty() {
            return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
                    && mOverscanBottom == 0
                    && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
                    && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
                    && mUserRotation == Surface.ROTATION_0;
        }
    }

@@ -90,13 +99,30 @@ class DisplaySettings {
        return entry;
    }

    private Entry getOrCreateEntry(String uniqueId, String name) {
        Entry entry = getEntry(uniqueId, name);
        if (entry == null) {
            entry = new Entry(uniqueId);
            mEntries.put(uniqueId, entry);
        }
        return entry;
    }

    private void removeEntryIfEmpty(String uniqueId, String name) {
        final Entry entry = getEntry(uniqueId, name);
        if (entry.isEmpty()) {
            mEntries.remove(uniqueId);
            mEntries.remove(name);
        }
    }

    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
        final Entry entry = getEntry(name, uniqueId);
        if (entry != null) {
            outRect.left = entry.overscanLeft;
            outRect.top = entry.overscanTop;
            outRect.right = entry.overscanRight;
            outRect.bottom = entry.overscanBottom;
            outRect.left = entry.mOverscanLeft;
            outRect.top = entry.mOverscanTop;
            outRect.right = entry.mOverscanRight;
            outRect.bottom = entry.mOverscanBottom;
        } else {
            outRect.set(0, 0, 0, 0);
        }
@@ -104,28 +130,22 @@ class DisplaySettings {

    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
            int bottom) {
        if (left == 0 && top == 0 && right == 0 && bottom == 0) {
            // Right now all we are storing is overscan; if there is no overscan,
            // we have no need for the entry.
            mEntries.remove(uniqueId);
            // Legacy name might have been in used, so we need to clear it.
            mEntries.remove(name);
            return;
        }
        Entry entry = mEntries.get(uniqueId);
        if (entry == null) {
            entry = new Entry(uniqueId);
            mEntries.put(uniqueId, entry);
        if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) {
            // All default value, no action needed.
            return;
        }
        entry.overscanLeft = left;
        entry.overscanTop = top;
        entry.overscanRight = right;
        entry.overscanBottom = bottom;
        entry = getOrCreateEntry(uniqueId, name);
        entry.mOverscanLeft = left;
        entry.mOverscanTop = top;
        entry.mOverscanRight = right;
        entry.mOverscanBottom = bottom;
        removeEntryIfEmpty(uniqueId, name);
    }

    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
        final Entry entry = getEntry(name, uniqueId);
        int windowingMode = entry != null ? entry.windowingMode
        int windowingMode = entry != null ? entry.mWindowingMode
                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
        // This display used to be in freeform, but we don't support freeform anymore, so fall
        // back to fullscreen.
@@ -148,6 +168,36 @@ class DisplaySettings {
        return windowingMode;
    }

    void setUserRotation(DisplayContent dc, int rotationMode, int rotation) {
        final DisplayInfo displayInfo = dc.getDisplayInfo();

        final String uniqueId = displayInfo.uniqueId;
        final String name = displayInfo.name;
        Entry entry = getEntry(displayInfo.name, uniqueId);
        if (rotationMode == WindowManagerPolicy.USER_ROTATION_FREE
                && rotation == Surface.ROTATION_0 && entry == null) {
            // All default values. No action needed.
            return;
        }

        entry = getOrCreateEntry(uniqueId, name);
        entry.mUserRotationMode = rotationMode;
        entry.mUserRotation = rotation;
        removeEntryIfEmpty(uniqueId, name);
    }

    private void restoreUserRotation(DisplayContent dc) {
        final DisplayInfo info = dc.getDisplayInfo();

        final Entry entry = getEntry(info.name, info.uniqueId);
        final int userRotationMode = entry != null ? entry.mUserRotationMode
                : WindowManagerPolicy.USER_ROTATION_FREE;
        final int userRotation = entry != null ? entry.mUserRotation
                : Surface.ROTATION_0;

        dc.getDisplayRotation().restoreUserRotation(userRotationMode, userRotation);
    }

    void applySettingsToDisplayLocked(DisplayContent dc) {
        final DisplayInfo displayInfo = dc.getDisplayInfo();

@@ -161,6 +211,8 @@ class DisplaySettings {
        displayInfo.overscanTop = rect.top;
        displayInfo.overscanRight = rect.right;
        displayInfo.overscanBottom = rect.bottom;

        restoreUserRotation(dc);
    }

    void readSettingsLocked() {
@@ -244,12 +296,16 @@ class DisplaySettings {
        String name = parser.getAttributeValue(null, "name");
        if (name != null) {
            Entry entry = new Entry(name);
            entry.overscanLeft = getIntAttribute(parser, "overscanLeft");
            entry.overscanTop = getIntAttribute(parser, "overscanTop");
            entry.overscanRight = getIntAttribute(parser, "overscanRight");
            entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
            entry.windowingMode = getIntAttribute(parser, "windowingMode",
            entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
            entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
            entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
            entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
            entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
                    WindowConfiguration.WINDOWING_MODE_UNDEFINED);
            entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
                    WindowManagerPolicy.USER_ROTATION_FREE);
            entry.mUserRotation = getIntAttribute(parser, "userRotation",
                    Surface.ROTATION_0);
            mEntries.put(name, entry);
        }
        XmlUtils.skipCurrentTag(parser);
@@ -272,21 +328,28 @@ class DisplaySettings {

            for (Entry entry : mEntries.values()) {
                out.startTag(null, "display");
                out.attribute(null, "name", entry.name);
                if (entry.overscanLeft != 0) {
                    out.attribute(null, "overscanLeft", Integer.toString(entry.overscanLeft));
                out.attribute(null, "name", entry.mName);
                if (entry.mOverscanLeft != 0) {
                    out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
                }
                if (entry.mOverscanTop != 0) {
                    out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
                }
                if (entry.mOverscanRight != 0) {
                    out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
                }
                if (entry.overscanTop != 0) {
                    out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop));
                if (entry.mOverscanBottom != 0) {
                    out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
                }
                if (entry.overscanRight != 0) {
                    out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight));
                if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                    out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
                }
                if (entry.overscanBottom != 0) {
                    out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
                if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
                    out.attribute(null, "userRotationMode",
                            Integer.toString(entry.mUserRotationMode));
                }
                if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                    out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
                if (entry.mUserRotation != Surface.ROTATION_0) {
                    out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
                }
                out.endTag(null, "display");
            }
Loading