Support rotation on secondary displays (3/N)

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

Create DisplayPolicy and move partial fields which
have dependency with rotation.

Bug: 111361251
Test: go/wm-smoke
Test: atest FrameworksServicesTests:AppWindowTokenTests
Test: atest FrameworksServicesTests:PhoneWindowManagerLayoutTest
Test: atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
Change-Id: I2dfd15ffcc41e10a9074087e6aa15975c92b4979
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 79d9b11..a8b92a6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -288,6 +288,7 @@
 import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.DisplayPolicy;
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowFrames;
 import com.android.server.wm.WindowManagerInternal;
@@ -501,8 +502,6 @@
     WindowState mStatusBar = null;
     private final int[] mStatusBarHeightForRotation = new int[4];
     WindowState mNavigationBar = null;
-    boolean mHasNavigationBar = false;
-    boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
     @NavigationBarPosition
     int mNavigationBarPosition = NAV_BAR_BOTTOM;
     int[] mNavigationBarHeightForRotationDefault = new int[4];
@@ -557,8 +556,6 @@
     volatile boolean mRecentsVisible;
     volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
     volatile boolean mPictureInPictureVisible;
-    // Written by vr manager thread, only read in this class.
-    volatile private boolean mPersistentVrModeEnabled;
     volatile private boolean mDismissImeOnBackKeyPressed;
 
     // Used to hold the last user key used to wake the device.  This helps us prevent up events
@@ -568,40 +565,18 @@
     int mRecentAppsHeldModifiers;
     boolean mLanguageSwitchKeyPressed;
 
-    int mLidState = LID_ABSENT;
     int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
     boolean mHaveBuiltInKeyboard;
 
     boolean mSystemReady;
     boolean mSystemBooted;
-    boolean mHdmiPlugged;
     HdmiControl mHdmiControl;
     IUiModeManager mUiModeManager;
     int mUiMode;
-    int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
-    int mLidOpenRotation;
-    int mCarDockRotation;
-    int mDeskDockRotation;
-    int mUndockedHdmiRotation;
-    int mDemoHdmiRotation;
-    boolean mDemoHdmiRotationLock;
-    int mDemoRotation;
-    boolean mDemoRotationLock;
 
     boolean mWakeGestureEnabledSetting;
     MyWakeGestureListener mWakeGestureListener;
 
-    // Default display does not rotate, apps that require non-default orientation will have to
-    // have the orientation emulated.
-    private boolean mForceDefaultOrientation = false;
-
-    int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
-    int mUserRotation = Surface.ROTATION_0;
-
-    boolean mSupportAutoRotation;
-    int mAllowAllRotations = -1;
-    boolean mCarDockEnablesAccelerometer;
-    boolean mDeskDockEnablesAccelerometer;
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
     boolean mLidControlsScreenLock;
@@ -614,12 +589,6 @@
     int mLongPressOnBackBehavior;
     int mShortPressOnSleepBehavior;
     int mShortPressOnWindowBehavior;
-    volatile boolean mAwake;
-    boolean mScreenOnEarly;
-    boolean mScreenOnFully;
-    ScreenOnListener mScreenOnListener;
-    boolean mKeyguardDrawComplete;
-    boolean mWindowManagerDrawComplete;
     boolean mHasSoftInput = false;
     boolean mTranslucentDecorEnabled = true;
     boolean mUseTvRouting;
@@ -728,16 +697,14 @@
     // Behavior of Back button while in-call and screen on
     int mIncallBackBehavior;
 
-    // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
-    int mShowRotationSuggestions;
-
     // Whether system navigation keys are enabled
     boolean mSystemNavigationKeysEnabled;
 
-    // TODO(multi-display): Remove default when the dependencies are multi-display ready.
+    // TODO(b/111361251): Remove default when the dependencies are multi-display ready.
     Display mDefaultDisplay;
     DisplayRotation mDefaultDisplayRotation;
-    MyOrientationListener mDefaultOrientationListener;
+    DisplayPolicy mDefaultDisplayPolicy;
+    WindowOrientationListener mDefaultOrientationListener;
 
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior;
@@ -960,7 +927,7 @@
     private UEventObserver mHDMIObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
-            setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
+            mDefaultDisplayPolicy.setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
         }
     };
 
@@ -985,12 +952,6 @@
                     Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.ACCELEROMETER_ROTATION), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.USER_ROTATION), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.SCREEN_OFF_TIMEOUT), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
@@ -1003,9 +964,6 @@
                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -1040,68 +998,11 @@
         }
     }
 
-    class MyOrientationListener extends WindowOrientationListener
-            implements WindowManagerPolicy.RotationSource {
-
-        private SparseArray<Runnable> mRunnableCache;
-        private boolean mEnabled;
-
-        MyOrientationListener(Context context, Handler handler) {
-            super(context, handler);
-            mRunnableCache = new SparseArray<>(5);
-        }
-
-        private class UpdateRunnable implements Runnable {
-            private final int mRotation;
-            UpdateRunnable(int rotation) {
-                mRotation = rotation;
-            }
-
-            @Override
-            public void run() {
-                // send interaction hint to improve redraw performance
-                mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
-                if (isRotationChoicePossible(mDefaultDisplayRotation.getCurrentAppOrientation())) {
-                    final boolean isValid = mDefaultDisplayRotation
-                            .isValidRotationChoice(mRotation);
-                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
-                } else {
-                    updateRotation(false);
-                }
-            }
-        }
-
-        @Override
-        public void onProposedRotationChanged(int rotation) {
-            if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
-            Runnable r = mRunnableCache.get(rotation, null);
-            if (r == null){
-                r = new UpdateRunnable(rotation);
-                mRunnableCache.put(rotation, r);
-            }
-            mHandler.post(r);
-        }
-
-        @Override
-        public void enable(boolean clearCurrentRotation) {
-            super.enable(clearCurrentRotation);
-            mEnabled = true;
-            if(localLOGV) Slog.v(TAG, "Enabling listeners");
-        }
-
-        @Override
-        public void disable() {
-            super.disable();
-            mEnabled = false;
-            if(localLOGV) Slog.v(TAG, "Disabling listeners");
-        }
-    }
-
     final IPersistentVrStateCallbacks mPersistentVrModeListener =
             new IPersistentVrStateCallbacks.Stub() {
         @Override
         public void onPersistentVrStateChanged(boolean enabled) {
-            mPersistentVrModeEnabled = enabled;
+            mDefaultDisplayPolicy.setPersistentVrModeEnabled(enabled);
         }
     };
 
@@ -1183,105 +1084,6 @@
         }
     }
 
-    /*
-     * We always let the sensor be switched on by default except when
-     * the user has explicitly disabled sensor based rotation or when the
-     * screen is switched off.
-     */
-    boolean needSensorRunningLp() {
-        if (mSupportAutoRotation) {
-            final int currentAppOrientation = mDefaultDisplayRotation.getCurrentAppOrientation();
-            if (currentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
-                    || currentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                    || currentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
-                    || currentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
-                // If the application has explicitly requested to follow the
-                // orientation, then we need to turn the sensor on.
-                return true;
-            }
-        }
-        if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) ||
-                (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
-            // enable accelerometer if we are docked in a dock that enables accelerometer
-            // orientation management,
-            return true;
-        }
-        if (mUserRotationMode == USER_ROTATION_LOCKED) {
-            // If the setting for using the sensor by default is enabled, then
-            // we will always leave it on.  Note that the user could go to
-            // a window that forces an orientation that does not use the
-            // sensor and in theory we could turn it off... however, when next
-            // turning it on we won't have a good value for the current
-            // orientation for a little bit, which can cause orientation
-            // changes to lag, so we'd like to keep it always on.  (It will
-            // still be turned off when the screen is off.)
-
-            // When locked we can provide rotation suggestions users can approve to change the
-            // current screen rotation. To do this the sensor needs to be running.
-            return mSupportAutoRotation &&
-                    mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
-        }
-        return mSupportAutoRotation;
-    }
-
-    @Override
-    public void updateOrientationListener() {
-        synchronized (mLock) {
-            updateOrientationListenerLp();
-        }
-    }
-
-    /*
-     * Various use cases for invoking this function
-     * screen turning off, should always disable listeners if already enabled
-     * screen turned on and current app has sensor based orientation, enable listeners
-     * if not already enabled
-     * screen turned on and current app does not have sensor orientation, disable listeners if
-     * already enabled
-     * screen turning on and current app has sensor based orientation, enable listeners if needed
-     * screen turning on and current app has nosensor based orientation, do nothing
-     */
-    void updateOrientationListenerLp() {
-        if (!mDefaultOrientationListener.canDetectOrientation()) {
-            // If sensor is turned off or nonexistent for some reason
-            return;
-        }
-        // Could have been invoked due to screen turning on or off or
-        // change of the currently visible window's orientation.
-        if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly
-                + ", mAwake=" + mAwake + ", mCurrentAppOrientation="
-                + mDefaultDisplayRotation.getCurrentAppOrientation()
-                + ", mOrientationSensorEnabled=" + mDefaultOrientationListener.mEnabled
-                + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
-                + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
-        boolean disable = true;
-        // Note: We postpone the rotating of the screen until the keyguard as well as the
-        // window manager have reported a draw complete or the keyguard is going away in dismiss
-        // mode.
-        if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {
-            if (needSensorRunningLp()) {
-                disable = false;
-                //enable listener if not already enabled
-                if (!mDefaultOrientationListener.mEnabled) {
-                    // Don't clear the current sensor orientation if the keyguard is going away in
-                    // dismiss mode. This allows window manager to use the last sensor reading to
-                    // determine the orientation vs. falling back to the last known orientation if
-                    // the sensor reading was cleared which can cause it to relaunch the app that
-                    // will show in the wrong orientation first before correcting leading to app
-                    // launch delays.
-                    mDefaultOrientationListener.enable(true /* clearCurrentRotation */);
-                }
-            }
-        }
-        //check if sensors need to be disabled
-        if (disable && mDefaultOrientationListener.mEnabled) {
-            mDefaultOrientationListener.disable();
-        }
-    }
-
     private void interceptBackKeyDown() {
         MetricsLogger.count(mContext, "key_back_down", 1);
         // Reset back key state for long press
@@ -1507,7 +1309,7 @@
     }
 
     private void powerPress(long eventTime, boolean interactive, int count) {
-        if (mScreenOnEarly && !mScreenOnFully) {
+        if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
             Slog.i(TAG, "Suppressed redundant power key press while "
                     + "already in the process of turning the screen on.");
             return;
@@ -2007,6 +1809,14 @@
         return mContext.getResources().getConfiguration().isScreenRound();
     }
 
+    @Override
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
+        mDefaultDisplay = displayContentInfo.getDisplay();
+        mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
+        mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
+        mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener();
+    }
+
     /** {@inheritDoc} */
     @Override
     public void init(Context context, IWindowManager windowManager,
@@ -2063,15 +1873,6 @@
 
         mHandler = new PolicyHandler();
         mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
-        final DisplayContentInfo displayContentInfo =
-                windowManagerFuncs.getDefaultDisplayContentInfo();
-        mDefaultDisplay = displayContentInfo.getDisplay();
-        mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
-        mDefaultOrientationListener = new MyOrientationListener(mContext, mHandler);
-        try {
-            mDefaultOrientationListener.setCurrentRotation(
-                    windowManager.getDefaultDisplayRotation());
-        } catch (RemoteException ex) { }
         mSettingsObserver = new SettingsObserver(mHandler);
         mSettingsObserver.observe();
         mShortcutManager = new ShortcutManager(context);
@@ -2102,20 +1903,6 @@
         mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mPowerKeyWakeLock");
         mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
-        mSupportAutoRotation = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_supportAutoRotation);
-        mLidOpenRotation = readRotation(
-                com.android.internal.R.integer.config_lidOpenRotation);
-        mCarDockRotation = readRotation(
-                com.android.internal.R.integer.config_carDockRotation);
-        mDeskDockRotation = readRotation(
-                com.android.internal.R.integer.config_deskDockRotation);
-        mUndockedHdmiRotation = readRotation(
-                com.android.internal.R.integer.config_undockedHdmiRotation);
-        mCarDockEnablesAccelerometer = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
-        mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
         mLidKeyboardAccessibility = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lidKeyboardAccessibility);
         mLidNavigationAccessibility = mContext.getResources().getInteger(
@@ -2189,8 +1976,8 @@
         Intent intent = context.registerReceiver(mDockReceiver, filter);
         if (intent != null) {
             // Retrieve current sticky dock event broadcast.
-            mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                    Intent.EXTRA_DOCK_STATE_UNDOCKED);
+            mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                    Intent.EXTRA_DOCK_STATE_UNDOCKED));
         }
 
         // register for dream-related broadcasts
@@ -2355,93 +2142,12 @@
                 com.android.internal.R.integer.config_navBarOpacityMode);
     }
 
-    @Override
-    public void setInitialDisplaySize(DisplayRotation displayRotation, int width, int height,
-            int density) {
-        // This method might be called before the policy has been fully initialized
-        // or for other displays we don't care about.
-        // TODO(multi-display): Define policy for secondary displays.
-        if (mContext == null || !displayRotation.isDefaultDisplay()) {
-            return;
-        }
-
-        final int shortSize;
-        final int longSize;
-        if (width > height) {
-            shortSize = height;
-            longSize = width;
-        } else {
-            shortSize = width;
-            longSize = height;
-        }
-
-        // SystemUI (status bar) layout policy
-        final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
-        final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
-
-        // Allow the navigation bar to move on non-square small devices (phones).
-        mNavigationBarCanMove = width != height && shortSizeDp < 600;
-
-        final Resources res = mContext.getResources();
-        mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
-
-        // Allow a system property to override this. Used by the emulator.
-        // See also hasNavigationBar().
-        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
-        if ("1".equals(navBarOverride)) {
-            mHasNavigationBar = false;
-        } else if ("0".equals(navBarOverride)) {
-            mHasNavigationBar = true;
-        }
-
-        // For demo purposes, allow the rotation of the HDMI display to be controlled.
-        // By default, HDMI locks rotation to landscape.
-        if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
-            mDemoHdmiRotation = displayRotation.getPortraitRotation();
-        } else {
-            mDemoHdmiRotation = displayRotation.getLandscapeRotation();
-        }
-        mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
-
-        // For demo purposes, allow the rotation of the remote display to be controlled.
-        // By default, remote display locks rotation to landscape.
-        if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
-            mDemoRotation = displayRotation.getPortraitRotation();
-        } else {
-            mDemoRotation = displayRotation.getLandscapeRotation();
-        }
-        mDemoRotationLock = SystemProperties.getBoolean(
-                "persist.demo.rotationlock", false);
-
-        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
-        // http://developer.android.com/guide/practices/screens_support.html#range
-        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
-        // so if the orientation is forced, we need to respect that no matter what.
-        final boolean isCar = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE);
-        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
-        // so if the orientation is forced, we need to respect that no matter what.
-        final boolean isTv = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_LEANBACK);
-        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
-                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
-                // For debug purposes the next line turns this feature off with:
-                // $ adb shell setprop config.override_forced_orient true
-                // $ adb shell wm size reset
-                !"true".equals(SystemProperties.get("config.override_forced_orient"));
-    }
-
     /**
      * @return whether the navigation bar can be hidden, e.g. the device has a
      *         navigation bar and touch exploration is not enabled
      */
     private boolean canHideNavigationBar() {
-        return mHasNavigationBar;
-    }
-
-    @Override
-    public boolean isDefaultOrientationForced() {
-        return mForceDefaultOrientation;
+        return mDefaultDisplayPolicy.hasNavigationBar();
     }
 
     public void updateSettings() {
@@ -2470,15 +2176,6 @@
                     .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
                 mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
             }
-            // Configure rotation suggestions.
-            int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
-                    UserHandle.USER_CURRENT);
-            if (mShowRotationSuggestions != showRotationSuggestions) {
-                mShowRotationSuggestions = showRotationSuggestions;
-                updateOrientationListenerLp(); // Enable, disable the orientation listener
-            }
 
             // Configure wake gesture.
             boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -2489,24 +2186,6 @@
                 updateWakeGestureListenerLp();
             }
 
-            // Configure rotation lock.
-            int userRotation = Settings.System.getIntForUser(resolver,
-                    Settings.System.USER_ROTATION, Surface.ROTATION_0,
-                    UserHandle.USER_CURRENT);
-            if (mUserRotation != userRotation) {
-                mUserRotation = userRotation;
-                updateRotation = true;
-            }
-            int userRotationMode = Settings.System.getIntForUser(resolver,
-                    Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
-                            WindowManagerPolicy.USER_ROTATION_FREE :
-                                    WindowManagerPolicy.USER_ROTATION_LOCKED;
-            if (mUserRotationMode != userRotationMode) {
-                mUserRotationMode = userRotationMode;
-                updateRotation = true;
-                updateOrientationListenerLp();
-            }
-
             if (mSystemReady) {
                 int pointerLocation = Settings.System.getIntForUser(resolver,
                         Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT);
@@ -2547,8 +2226,8 @@
     }
 
     private boolean shouldEnableWakeGestureLp() {
-        return mWakeGestureEnabledSetting && !mAwake
-                && (!mLidControlsSleep || mLidState != LID_CLOSED)
+        return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
+                && (!mLidControlsSleep || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
                 && mWakeGestureListener.isSupported();
     }
 
@@ -2588,24 +2267,6 @@
         }
     }
 
-    private int readRotation(int resID) {
-        try {
-            int rotation = mContext.getResources().getInteger(resID);
-            switch (rotation) {
-                case 0:
-                    return Surface.ROTATION_0;
-                case 90:
-                    return Surface.ROTATION_90;
-                case 180:
-                    return Surface.ROTATION_180;
-                case 270:
-                    return Surface.ROTATION_270;
-            }
-        } catch (Resources.NotFoundException e) {
-            // fall through
-        }
-        return -1;
-    }
 
     /** {@inheritDoc} */
     @Override
@@ -2834,7 +2495,7 @@
     }
 
     void readLidState() {
-        mLidState = mWindowManagerFuncs.getLidState();
+        mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
     }
 
     private void readCameraLensCoverState() {
@@ -2842,11 +2503,12 @@
     }
 
     private boolean isHidden(int accessibilityMode) {
+        final int lidState = mDefaultDisplayPolicy.getLidState();
         switch (accessibilityMode) {
             case 1:
-                return mLidState == LID_CLOSED;
+                return lidState == LID_CLOSED;
             case 2:
-                return mLidState == LID_OPEN;
+                return lidState == LID_OPEN;
             default:
                 return false;
         }
@@ -2886,7 +2548,7 @@
     public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
         final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation();
         // TODO(multi-display): Define policy for secondary displays.
-        if (!displayRotation.isDefaultDisplay()) {
+        if (!displayRotation.isDefaultDisplay) {
             return;
         }
 
@@ -2962,10 +2624,10 @@
             int displayId, DisplayCutout displayCutout) {
         int width = fullWidth;
         // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
             // For a basic navigation bar, when we are in landscape mode we place
             // the navigation bar to the side.
-            if (mNavigationBarCanMove && fullWidth > fullHeight) {
+            if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) {
                 width -= getNavigationBarWidth(rotation, uiMode);
             }
         }
@@ -2988,10 +2650,10 @@
             int displayId, DisplayCutout displayCutout) {
         int height = fullHeight;
         // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
             // For a basic navigation bar, when we are in portrait mode we place
             // the navigation bar to the bottom.
-            if (!mNavigationBarCanMove || fullWidth < fullHeight) {
+            if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) {
                 height -= getNavigationBarHeight(rotation, uiMode);
             }
         }
@@ -3079,8 +2741,8 @@
         // In that case, we want to continue hiding the IME until the windows have completed
         // drawing. This way, we know that the IME can be safely shown since the other windows are
         // now shown.
-        final boolean hideIme =
-                win.isInputMethodWindow() && (mAodShowing || !mWindowManagerDrawComplete);
+        final boolean hideIme = win.isInputMethodWindow()
+                && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
         return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
                 || hideDockDivider || hideIme;
     }
@@ -3458,7 +3120,7 @@
     @Override
     public void selectRotationAnimationLw(int anim[]) {
         // If the screen is off or non-interactive, force a jumpcut.
-        final boolean forceJumpcut = !mScreenOnFully || !okToAnimate();
+        final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate();
         if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
                 + mTopFullscreenOpaqueWindowState + " rotationAnimation="
                 + (mTopFullscreenOpaqueWindowState == null ?
@@ -3865,7 +3527,7 @@
             // If the device is in VR mode and keys are "internal" (e.g. on the side of the
             // device), then drop the volume keys and don't forward it to the application/dispatch
             // the audio event.
-            if (mPersistentVrModeEnabled) {
+            if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
                 final InputDevice d = event.getDevice();
                 if (d != null && !d.isExternal()) {
                     return -1;
@@ -4955,7 +4617,7 @@
 
     @NavigationBarPosition
     private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
-        if (mNavigationBarCanMove && displayWidth > displayHeight) {
+        if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) {
             if (displayRotation == Surface.ROTATION_270) {
                 return NAV_BAR_LEFT;
             } else {
@@ -5089,7 +4751,7 @@
         mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
         mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
 
-        final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
+        final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
                 && mNavigationBar != null && mNavigationBar.isVisibleLw());
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
@@ -5898,11 +5560,11 @@
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
         // lid changed state
         final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED;
-        if (newLidState == mLidState) {
+        if (newLidState == mDefaultDisplayPolicy.getLidState()) {
             return;
         }
 
-        mLidState = newLidState;
+        mDefaultDisplayPolicy.setLidState(newLidState);
         applyLidSwitchState();
         updateRotation(true);
 
@@ -5937,17 +5599,6 @@
         mCameraLensCoverState = lensCoverState;
     }
 
-    void setHdmiPlugged(boolean plugged) {
-        if (mHdmiPlugged != plugged) {
-            mHdmiPlugged = plugged;
-            updateRotation(true, true);
-            Intent intent = new Intent(ACTION_HDMI_PLUGGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-        }
-    }
-
     void initializeHdmiState() {
         final int oldMask = StrictMode.allowThreadDiskReadsMask();
         try {
@@ -5987,8 +5638,7 @@
         }
         // This dance forces the code in setHdmiPlugged to run.
         // Always do this so the sticky intent is stuck (to false) if there is no hdmi.
-        mHdmiPlugged = !plugged;
-        setHdmiPlugged(!mHdmiPlugged);
+        mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
     }
 
 
@@ -6428,16 +6078,6 @@
     }
 
     /**
-     * Notify the StatusBar that system rotation suggestion has changed.
-     */
-    private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
-        StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
-        if (statusBar != null) {
-            statusBar.onProposedRotationChanged(rotation, isValid);
-        }
-    }
-
-    /**
      * Returns true if the key can have global actions attached to it.
      * We reserve all power management keys for the system since they require
      * very careful handling.
@@ -6466,7 +6106,7 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE:
-                return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+                return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
             // ignore media and camera keys
             case KeyEvent.KEYCODE_MUTE:
@@ -6665,8 +6305,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
-                mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                        Intent.EXTRA_DOCK_STATE_UNDOCKED));
             } else {
                 try {
                     IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
@@ -6676,9 +6316,7 @@
                 }
             }
             updateRotation(true);
-            synchronized (mLock) {
-                updateOrientationListenerLp();
-            }
+            mDefaultDisplayRotation.updateOrientationListener();
         }
     };
 
@@ -6706,6 +6344,7 @@
                 // and then updates our own bookkeeping based on the now-
                 // current user.
                 mSettingsObserver.onChange(false);
+                mDefaultDisplayRotation.onUserSwitch();
 
                 // force a re-application of focused window sysui visibility.
                 // the window may never have been shown for this user
@@ -6779,15 +6418,16 @@
 
         mGoingToSleep = false;
         mRequestedOrGoingToSleep = false;
+        mDefaultDisplayPolicy.setAwake(false);
 
         // We must get this work done here because the power manager will drop
         // the wake lock and let the system suspend once this function returns.
         synchronized (mLock) {
-            mAwake = false;
             updateWakeGestureListenerLp();
-            updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
+        mDefaultDisplayRotation.updateOrientationListener();
+
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onFinishedGoingToSleep(why,
                     mCameraGestureTriggeredDuringGoingToSleep);
@@ -6801,17 +6441,17 @@
         EventLog.writeEvent(70000, 1);
         if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
 
+        mDefaultDisplayPolicy.setAwake(true);
+
         // Since goToSleep performs these functions synchronously, we must
         // do the same here.  We cannot post this work to a handler because
         // that might cause it to become reordered with respect to what
         // may happen in a future call to goToSleep.
         synchronized (mLock) {
-            mAwake = true;
-
             updateWakeGestureListenerLp();
-            updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
+        mDefaultDisplayRotation.updateOrientationListener();
 
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedWakingUp();
@@ -6848,16 +6488,14 @@
     }
 
     private void finishKeyguardDrawn() {
-        synchronized (mLock) {
-            if (!mScreenOnEarly || mKeyguardDrawComplete) {
-                return; // We are not awake yet or we have already informed of this event.
-            }
+        if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
+            return;
+        }
 
-            mKeyguardDrawComplete = true;
+        synchronized (mLock) {
             if (mKeyguardDelegate != null) {
                 mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
             }
-            mWindowManagerDrawComplete = false;
         }
 
         // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
@@ -6872,18 +6510,13 @@
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
 
         updateScreenOffSleepToken(true);
+        mDefaultDisplayPolicy.screenTurnedOff();
         synchronized (mLock) {
-            mScreenOnEarly = false;
-            mScreenOnFully = false;
-            mKeyguardDrawComplete = false;
-            mWindowManagerDrawComplete = false;
-            mScreenOnListener = null;
-            updateOrientationListenerLp();
-
             if (mKeyguardDelegate != null) {
                 mKeyguardDelegate.onScreenTurnedOff();
             }
         }
+        mDefaultDisplayRotation.updateOrientationListener();
         reportScreenStateToVrManager(false);
     }
 
@@ -6900,13 +6533,9 @@
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
 
         updateScreenOffSleepToken(false);
-        synchronized (mLock) {
-            mScreenOnEarly = true;
-            mScreenOnFully = false;
-            mKeyguardDrawComplete = false;
-            mWindowManagerDrawComplete = false;
-            mScreenOnListener = screenOnListener;
+        mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
 
+        synchronized (mLock) {
             if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
                 mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                 mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
@@ -6949,46 +6578,29 @@
     }
 
     private void finishWindowsDrawn() {
-        synchronized (mLock) {
-            if (!mScreenOnEarly || mWindowManagerDrawComplete) {
-                return; // Screen is not turned on or we did already handle this case earlier.
-            }
-
-            mWindowManagerDrawComplete = true;
+        if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
+            return;
         }
 
         finishScreenTurningOn();
     }
 
     private void finishScreenTurningOn() {
-        synchronized (mLock) {
-            // We have just finished drawing screen content. Since the orientation listener
-            // gets only installed when all windows are drawn, we try to install it again.
-            updateOrientationListenerLp();
+        // We have just finished drawing screen content. Since the orientation listener
+        // gets only installed when all windows are drawn, we try to install it again.
+        mDefaultDisplayRotation.updateOrientationListener();
+
+        final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
+        if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
+            return; // Spurious or not ready yet.
         }
-        final ScreenOnListener listener;
+
         final boolean enableScreen;
+        final boolean awake = mDefaultDisplayPolicy.isAwake();
         synchronized (mLock) {
-            if (DEBUG_WAKEUP) Slog.d(TAG,
-                    "finishScreenTurningOn: mAwake=" + mAwake
-                            + ", mScreenOnEarly=" + mScreenOnEarly
-                            + ", mScreenOnFully=" + mScreenOnFully
-                            + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
-                            + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
-            if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
-                    || (mAwake && !mKeyguardDrawComplete)) {
-                return; // spurious or not ready yet
-            }
-
-            if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on...");
-            listener = mScreenOnListener;
-            mScreenOnListener = null;
-            mScreenOnFully = true;
-
             // Remember the first time we draw the keyguard so we know when we're done with
             // the main part of booting and can enable the screen and hide boot messages.
-            if (!mKeyguardDrawnOnce && mAwake) {
+            if (!mKeyguardDrawnOnce && awake) {
                 mKeyguardDrawnOnce = true;
                 enableScreen = true;
                 if (mBootMessageNeedsHiding) {
@@ -7029,14 +6641,12 @@
 
     @Override
     public boolean isScreenOn() {
-        synchronized (mLock) {
-            return mScreenOnEarly;
-        }
+        return mDefaultDisplayPolicy.isScreenOnEarly();
     }
 
     @Override
     public boolean okToAnimate() {
-        return mAwake && !mGoingToSleep;
+        return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
     }
 
     /** {@inheritDoc} */
@@ -7146,7 +6756,7 @@
         outInsets.setEmpty();
 
         // Only navigation bar
-        if (mHasNavigationBar) {
+        if (mDefaultDisplayPolicy.hasNavigationBar()) {
             int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
             if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
@@ -7180,7 +6790,8 @@
     public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
             int displayHeight, int displayRotation) {
         final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
-        return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+        return isDockSideAllowed(dockSide, originalDockSide, barPosition,
+                mDefaultDisplayPolicy.navigationBarCanMove());
     }
 
     @VisibleForTesting
@@ -7217,186 +6828,6 @@
     }
 
     @Override
-    public int rotationForOrientationLw(DisplayRotation displayRotation, int orientation,
-            int lastRotation) {
-        if (false) {
-            Slog.v(TAG, "rotationForOrientationLw(orient="
-                        + orientation + ", last=" + lastRotation
-                        + "); user=" + mUserRotation + " "
-                        + ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
-                            ? "USER_ROTATION_LOCKED" : "")
-                        );
-        }
-
-        if (mForceDefaultOrientation) {
-            return Surface.ROTATION_0;
-        }
-
-        synchronized (mLock) {
-            int sensorRotation = displayRotation.getSensorRotation(); // may be -1
-            if (sensorRotation < 0) {
-                sensorRotation = lastRotation;
-            }
-
-            final int preferredRotation;
-            if (!displayRotation.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;
-            } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
-                // Ignore sensor when lid switch is open and rotation is forced.
-                preferredRotation = mLidOpenRotation;
-            } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
-                    && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
-                // Ignore sensor when in car dock unless explicitly enabled.
-                // This case can override the behavior of NOSENSOR, and can also
-                // enable 180 degree rotation while docked.
-                preferredRotation = mCarDockEnablesAccelerometer
-                        ? sensorRotation : mCarDockRotation;
-            } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                    || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                    || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
-                    && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
-                // Ignore sensor when in desk dock unless explicitly enabled.
-                // This case can override the behavior of NOSENSOR, and can also
-                // enable 180 degree rotation while docked.
-                preferredRotation = mDeskDockEnablesAccelerometer
-                        ? sensorRotation : mDeskDockRotation;
-            } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
-                // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
-                // Note that the dock orientation overrides the HDMI orientation.
-                preferredRotation = mDemoHdmiRotation;
-            } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
-                    && mUndockedHdmiRotation >= 0) {
-                // Ignore sensor when plugged into HDMI and an undocked orientation has
-                // been specified in the configuration (only for legacy devices without
-                // full multi-display support).
-                // Note that the dock orientation overrides the HDMI orientation.
-                preferredRotation = mUndockedHdmiRotation;
-            } else if (mDemoRotationLock) {
-                // Ignore sensor when demo rotation lock is enabled.
-                // Note that the dock orientation and HDMI rotation lock override this.
-                preferredRotation = mDemoRotation;
-            } else if (mPersistentVrModeEnabled) {
-                // While in VR, apps always prefer a portrait rotation. This does not change
-                // any apps that explicitly set landscape, but does cause sensors be ignored,
-                // and ignored any orientation lock that the user has set (this conditional
-                // should remain above the ORIENTATION_LOCKED conditional below).
-                preferredRotation = displayRotation.getPortraitRotation();
-            } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
-                // Application just wants to remain locked in the last rotation.
-                preferredRotation = lastRotation;
-            } else if (!mSupportAutoRotation) {
-                // If we don't support auto-rotation then bail out here and ignore
-                // the sensor and any rotation lock settings.
-                preferredRotation = -1;
-            } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                            && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
-                // Otherwise, use sensor only if requested by the application or enabled
-                // by default for USER or UNSPECIFIED modes.  Does not apply to NOSENSOR.
-                if (mAllowAllRotations < 0) {
-                    // Can't read this during init() because the context doesn't
-                    // have display metrics at that time so we cannot determine
-                    // tablet vs. phone then.
-                    mAllowAllRotations = mContext.getResources().getBoolean(
-                            com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
-                }
-                if (sensorRotation != Surface.ROTATION_180
-                        || mAllowAllRotations == 1
-                        || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                        || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
-                    preferredRotation = sensorRotation;
-                } else {
-                    preferredRotation = lastRotation;
-                }
-            } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
-                    && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
-                // Apply rotation lock.  Does not apply to NOSENSOR.
-                // The idea is that the user rotation expresses a weak preference for the direction
-                // of gravity and as NOSENSOR is never affected by gravity, then neither should
-                // NOSENSOR be affected by rotation lock (although it will be affected by docks).
-                preferredRotation = mUserRotation;
-            } else {
-                // No overriding preference.
-                // We will do exactly what the application asked us to do.
-                preferredRotation = -1;
-            }
-
-            return displayRotation.rotationForOrientation(orientation, lastRotation,
-                    preferredRotation);
-        }
-    }
-
-    @Override
-    public WindowManagerPolicy.RotationSource getRotationSource(int displayId) {
-        // TODO(multi-display): Distinguish sensor for different displays.
-        return displayId == DEFAULT_DISPLAY ? mDefaultOrientationListener : null;
-    }
-
-    public boolean isRotationChoicePossible(int orientation) {
-        // Rotation choice is only shown when the user is in locked mode.
-        if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
-        // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
-        // demo, hdmi, vr, etc mode
-
-        // Determine if the rotation is currently forced
-        if (mForceDefaultOrientation) {
-            return false; // Rotation is forced to default orientation
-
-        } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
-            return false; // Rotation is forced mLidOpenRotation
-
-        } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && !mCarDockEnablesAccelerometer) {
-            return false; // Rotation forced to mCarDockRotation
-
-        } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
-                && !mDeskDockEnablesAccelerometer) {
-            return false; // Rotation forced to mDeskDockRotation
-
-        } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
-            return false; // Rotation forced to mDemoHdmiRotation
-
-        } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
-                && mUndockedHdmiRotation >= 0) {
-            return false; // Rotation forced to mUndockedHdmiRotation
-
-        } else if (mDemoRotationLock) {
-            return false; // Rotation forced to mDemoRotation
-
-        } else if (mPersistentVrModeEnabled) {
-            return false; // Rotation forced to mPortraitRotation
-
-        } else if (!mSupportAutoRotation) {
-            return false;
-        }
-
-        // Ensure that some rotation choice is possible for the given orientation
-        switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
-            case ActivityInfo.SCREEN_ORIENTATION_USER:
-            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
-            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                // NOSENSOR description is ambiguous, in reality WM ignores user choice
-                return true;
-        }
-
-        // Rotation is forced, should be controlled by system
-        return false;
-    }
-
-    @Override
     public int getUserRotationMode() {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
@@ -7468,8 +6899,8 @@
 
         readCameraLensCoverState();
         updateUiMode();
+        mDefaultDisplayRotation.updateOrientationListener();
         synchronized (mLock) {
-            updateOrientationListenerLp();
             mSystemReady = true;
             mHandler.post(new Runnable() {
                 @Override
@@ -7507,9 +6938,7 @@
 
     @Override
     public boolean canDismissBootAnimation() {
-        synchronized (mLock) {
-            return mKeyguardDrawComplete;
-        }
+        return mDefaultDisplayPolicy.isKeyguardDrawComplete();
     }
 
     ProgressDialog mBootMsgDialog = null;
@@ -7609,7 +7038,7 @@
             }
         }
 
-        if (mAwake && mNotifyUserActivity) {
+        if (mDefaultDisplayPolicy.isAwake() && mNotifyUserActivity) {
             mHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY,
                     USER_ACTIVITY_NOTIFICATION_DELAY);
             mNotifyUserActivity = false;
@@ -7652,7 +7081,7 @@
 
     private void updateLockScreenTimeout() {
         synchronized (mScreenLockTimeout) {
-            boolean enable = (mAllowLockscreenWhenOn && mAwake &&
+            final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() &&
                     mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId));
             if (mLockScreenTimerActive != enable) {
                 if (enable) {
@@ -7707,10 +7136,11 @@
     }
 
     private void applyLidSwitchState() {
-        if (mLidState == LID_CLOSED && mLidControlsSleep) {
+        final int lidState = mDefaultDisplayPolicy.getLidState();
+        if (lidState == LID_CLOSED && mLidControlsSleep) {
             goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
-        } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
+        } else if (lidState == LID_CLOSED && mLidControlsScreenLock) {
             mWindowManagerFuncs.lockDeviceNow();
         }
 
@@ -7732,17 +7162,8 @@
 
     void updateRotation(boolean alwaysSendConfiguration) {
         try {
-            //set orientation on WindowManager
-            mWindowManager.updateRotation(alwaysSendConfiguration, false);
-        } catch (RemoteException e) {
-            // Ignore
-        }
-    }
-
-    void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
-        try {
-            //set orientation on WindowManager
-            mWindowManager.updateRotation(alwaysSendConfiguration, forceRelayout);
+            // Set orientation on WindowManager.
+            mWindowManager.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
         } catch (RemoteException e) {
             // Ignore
         }
@@ -7775,12 +7196,14 @@
             if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
                 intent = mDeskDockIntent;
             }
-        } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH
-                && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK)) {
-            // Always launch dock home from home when watch is docked, if it exists.
-            intent = mDeskDockIntent;
+        } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
+            final int dockMode = mDefaultDisplayPolicy.getDockMode();
+            if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                    || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
+                    || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
+                // Always launch dock home from home when watch is docked, if it exists.
+                intent = mDeskDockIntent;
+            }
         } else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
             if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
                 intent = mVrHeadsetHomeIntent;
@@ -8221,7 +7644,8 @@
         final long now = SystemClock.uptimeMillis();
         final boolean pendingPanic = mPendingPanicGestureUptime != 0
                 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
-        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
+        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
+                && mDefaultDisplayPolicy.isKeyguardDrawComplete()) {
             // The user performed the panic gesture recently, we're about to hide the bars,
             // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
             mPendingPanicGestureUptime = 0;
@@ -8364,7 +7788,7 @@
     // overridden by qemu.hw.mainkeys in the emulator.
     @Override
     public boolean hasNavigationBar() {
-        return mHasNavigationBar;
+        return mDefaultDisplayPolicy.hasNavigationBar();
     }
 
     @Override
@@ -8423,7 +7847,7 @@
         // If the navigation bar can't change sides, then it will
         // jump when we change orientations and we don't rotate
         // seamlessly.
-        if (!mNavigationBarCanMove) {
+        if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
             return false;
         }
         int delta = newRotation - oldRotation;
@@ -8457,12 +7881,13 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
-        proto.write(ROTATION_MODE, mUserRotationMode);
-        proto.write(ROTATION, mUserRotation);
+        proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
+        proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
         proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
-        proto.write(SCREEN_ON_FULLY, mScreenOnFully);
-        proto.write(KEYGUARD_DRAW_COMPLETE, mKeyguardDrawComplete);
-        proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mWindowManagerDrawComplete);
+        proto.write(SCREEN_ON_FULLY, mDefaultDisplayPolicy.isScreenOnFully());
+        proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
+        proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
+                mDefaultDisplayPolicy.isWindowManagerDrawComplete());
         if (mFocusedApp != null) {
             proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
         }
@@ -8498,13 +7923,8 @@
         pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
                 pw.print(" mSystemReady="); pw.print(mSystemReady);
                 pw.print(" mSystemBooted="); pw.println(mSystemBooted);
-        pw.print(prefix); pw.print("mLidState=");
-                pw.print(WindowManagerFuncs.lidStateToString(mLidState));
-                pw.print(" mLidOpenRotation=");
-                pw.println(Surface.rotationToString(mLidOpenRotation));
         pw.print(prefix); pw.print("mCameraLensCoverState=");
-                pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
-                pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
+                pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
         if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
                 || mForceClearedSystemUiFlags != 0) {
             pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
@@ -8522,26 +7942,9 @@
                 pw.println(mWakeGestureEnabledSetting);
 
         pw.print(prefix);
-                pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation);
-                pw.print(" mOrientationSensorEnabled=");
-                pw.println(mDefaultOrientationListener.mEnabled);
-        pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode));
-                pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode));
-        pw.print(prefix); pw.print("mEnableCarDockHomeCapture=");
-                pw.print(mEnableCarDockHomeCapture);
-                pw.print(" mCarDockRotation=");
-                pw.print(Surface.rotationToString(mCarDockRotation));
-                pw.print(" mDeskDockRotation=");
-                pw.println(Surface.rotationToString(mDeskDockRotation));
-        pw.print(prefix); pw.print("mUserRotationMode=");
-                pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
-                pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation));
-                pw.print(" mAllowAllRotations=");
-                pw.println(allowAllRotationsToString(mAllowAllRotations));
-        pw.print(prefix); pw.print("mCarDockEnablesAccelerometer=");
-                pw.print(mCarDockEnablesAccelerometer);
-                pw.print(" mDeskDockEnablesAccelerometer=");
-                pw.println(mDeskDockEnablesAccelerometer);
+                pw.print("mUiMode=");
+                pw.print(Configuration.uiModeToString(mUiMode));
+                pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
         pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
                 pw.print(mLidKeyboardAccessibility);
                 pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
@@ -8591,12 +7994,6 @@
                 pw.print(" mEndcallBehavior=");
                 pw.println(endcallBehaviorToString(mEndcallBehavior));
         pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
-        pw.print(prefix);
-                pw.print("mAwake="); pw.print(mAwake);
-                pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
-                pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
-        pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
-                pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
         pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
                 pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
@@ -8651,11 +8048,6 @@
         pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
-        pw.print(prefix); pw.print("mDemoHdmiRotation=");
-                pw.print(Surface.rotationToString(mDemoHdmiRotation));
-                pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
-        pw.print(prefix); pw.print("mUndockedHdmiRotation=");
-                pw.println(Surface.rotationToString(mUndockedHdmiRotation));
         if (mHasFeatureLeanback) {
             pw.print(prefix);
             pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
@@ -8687,19 +8079,6 @@
         mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
     }
 
-    private static String allowAllRotationsToString(int allowAll) {
-        switch (allowAll) {
-            case -1:
-                return "unknown";
-            case 0:
-                return "false";
-            case 1:
-                return "true";
-            default:
-                return Integer.toString(allowAll);
-        }
-    }
-
     private static String endcallBehaviorToString(int behavior) {
         StringBuilder sb = new StringBuilder();
         if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 70b2165..cc39217 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -691,6 +691,11 @@
     public final int USER_ROTATION_LOCKED = 1;
 
     /**
+     * Set the default display content to provide basic functions for the policy.
+     */
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo);
+
+    /**
      * Perform initialization of the policy.
      *
      * @param context The system context we are running in.
@@ -699,18 +704,6 @@
             WindowManagerFuncs windowManagerFuncs);
 
     /**
-     * @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true.
-     */
-    public boolean isDefaultOrientationForced();
-
-    /**
-     * Called by window manager once it has the initial, default native
-     * display dimensions.
-     */
-    public void setInitialDisplaySize(DisplayRotation displayRotation, int width, int height,
-            int density);
-
-    /**
      * Check permissions when adding a window.
      *
      * @param attrs The window's LayoutParams.
@@ -1433,28 +1426,6 @@
     public boolean isShowingDreamLw();
 
     /**
-     * Given an orientation constant, returns the appropriate surface rotation,
-     * taking into account sensors, docking mode, rotation lock, and other factors.
-     *
-     * @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.
-     */
-    public int rotationForOrientationLw(DisplayRotation displayRotation,
-            @ActivityInfo.ScreenOrientation int orientation, int lastRotation);
-
-    public void updateOrientationListener();
-
-    /**
-     * Get rotation source for the given display id.
-     */
-    public RotationSource getRotationSource(int displayId);
-
-    /**
      * Called when the system is mostly done booting to set whether
      * the system should go into safe mode.
      */
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 1508c9e..d5adb5e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -204,6 +204,10 @@
         }
     }
 
+    public Handler getHandler() {
+        return mHandler;
+    }
+
     /**
      * Sets the current rotation.
      *
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 605705e..ec2584b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -235,7 +235,8 @@
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
-    final DisplayRotation mDisplayRotation;
+    private final DisplayPolicy mDisplayPolicy;
+    private DisplayRotation mDisplayRotation;
     DisplayFrames mDisplayFrames;
 
     /**
@@ -306,8 +307,11 @@
     private boolean mLayoutNeeded;
     int pendingLayoutChanges;
     int mDeferredRotationPauseCount;
+
     // TODO(multi-display): remove some of the usages.
+    @VisibleForTesting
     boolean isDefaultDisplay;
+
     /**
      * Flag indicating whether WindowManager should override info for this display in
      * DisplayManager.
@@ -764,7 +768,13 @@
         mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
         initializeDisplayBaseInfo();
-        mDisplayRotation = new DisplayRotation(this, mService.mPolicy, mService.mContext);
+        mDisplayPolicy = new DisplayPolicy(service);
+        mDisplayRotation = new DisplayRotation(service, this);
+        if (isDefaultDisplay) {
+            // The policy may be invoked right after here, so it requires the necessary default
+            // fields of this display content.
+            mService.mPolicy.setDefaultDisplay(this);
+        }
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
@@ -921,11 +931,20 @@
         return mDisplayMetrics;
     }
 
+    DisplayPolicy getDisplayPolicy() {
+        return mDisplayPolicy;
+    }
+
     @Override
     public DisplayRotation getDisplayRotation() {
         return mDisplayRotation;
     }
 
+    @VisibleForTesting
+    void setDisplayRotation(DisplayRotation displayRotation) {
+        mDisplayRotation = displayRotation;
+    }
+
     int getRotation() {
         return mRotation;
     }
@@ -979,10 +998,7 @@
 
         mDeferredRotationPauseCount--;
         if (mDeferredRotationPauseCount == 0) {
-            final boolean changed = updateRotationUnchecked();
-            if (changed) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
-            }
+            updateRotationAndSendNewConfigIfNeeded();
         }
     }
 
@@ -998,8 +1014,7 @@
         final int oldRotation = getRotation();
         final boolean oldAltOrientation = getAltOrientation();
 
-        final int rotation = mService.mPolicy.rotationForOrientationLw(
-                mDisplayRotation, lastOrientation, oldRotation);
+        final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
         final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
                 lastOrientation, rotation);
         if (oldRotation == rotation && oldAltOrientation == altOrientation) {
@@ -1009,6 +1024,19 @@
     }
 
     /**
+     * Update rotation of the display and send configuration if the rotation is changed.
+     *
+     * @return {@code true} if the rotation has been changed and the new config is sent.
+     */
+    boolean updateRotationAndSendNewConfigIfNeeded() {
+        final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
+        if (changed) {
+            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+        }
+        return changed;
+    }
+
+    /**
      * Update rotation of the display.
      *
      * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
@@ -1066,8 +1094,7 @@
         final int oldRotation = mRotation;
         final int lastOrientation = mLastOrientation;
         final boolean oldAltOrientation = mAltOrientation;
-        final int rotation = mService.mPolicy.rotationForOrientationLw(mDisplayRotation,
-                lastOrientation, oldRotation);
+        final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
                 + mDisplayId + " based on lastOrientation=" + lastOrientation
                 + " and oldRotation=" + oldRotation);
@@ -1220,7 +1247,24 @@
     }
 
     void configureDisplayPolicy() {
-        mDisplayRotation.configure();
+        final int width = mBaseDisplayWidth;
+        final int height = mBaseDisplayHeight;
+        final int shortSize;
+        final int longSize;
+        if (width > height) {
+            shortSize = height;
+            longSize = width;
+        } else {
+            shortSize = width;
+            longSize = height;
+        }
+
+        final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+        final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+
+        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
+        mDisplayPolicy.configure(width, height, shortSizeDp);
+
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
     }
@@ -2394,6 +2438,8 @@
         pw.println();
         mDisplayFrames.dump(prefix, pw);
         pw.println();
+        mDisplayPolicy.dump(prefix, pw);
+        pw.println();
         mDisplayRotation.dump(prefix, pw);
         pw.println();
         mInputMonitor.dump(pw, "  ");
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
new file mode 100644
index 0000000..9151ddf
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -0,0 +1,260 @@
+/*
+ * 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 static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
+import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
+import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
+
+import java.io.PrintWriter;
+
+/**
+ * The policy that provides the basic behaviors and states of a display to show UI.
+ */
+public class DisplayPolicy {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
+
+    private final WindowManagerService mService;
+    private final Object mLock;
+
+    private final boolean mCarDockEnablesAccelerometer;
+    private final boolean mDeskDockEnablesAccelerometer;
+
+    private volatile int mLidState = LID_ABSENT;
+    private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    private volatile boolean mHdmiPlugged;
+
+    private volatile boolean mHasNavigationBar;
+    // Can the navigation bar ever move to the side?
+    private volatile boolean mNavigationBarCanMove;
+
+    // Written by vr manager thread, only read in this class.
+    private volatile boolean mPersistentVrModeEnabled;
+
+    private volatile boolean mAwake;
+    private volatile boolean mScreenOnEarly;
+    private volatile boolean mScreenOnFully;
+    private volatile ScreenOnListener mScreenOnListener;
+
+    private volatile boolean mKeyguardDrawComplete;
+    private volatile boolean mWindowManagerDrawComplete;
+
+    DisplayPolicy(WindowManagerService service) {
+        mService = service;
+        mLock = service.getWindowManagerLock();
+        mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
+        mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+    }
+
+    void configure(int width, int height, int shortSizeDp) {
+        // Allow the navigation bar to move on non-square small devices (phones).
+        mNavigationBarCanMove = width != height && shortSizeDp < 600;
+
+        mHasNavigationBar = mService.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_showNavigationBar);
+
+        // Allow a system property to override this. Used by the emulator.
+        // See also hasNavigationBar().
+        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+        if ("1".equals(navBarOverride)) {
+            mHasNavigationBar = false;
+        } else if ("0".equals(navBarOverride)) {
+            mHasNavigationBar = true;
+        }
+    }
+
+    public void setHdmiPlugged(boolean plugged) {
+        setHdmiPlugged(plugged, false /* force */);
+    }
+
+    public void setHdmiPlugged(boolean plugged, boolean force) {
+        if (force || mHdmiPlugged != plugged) {
+            mHdmiPlugged = plugged;
+            mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);
+            final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
+            mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
+    boolean isHdmiPlugged() {
+        return mHdmiPlugged;
+    }
+
+    boolean isCarDockEnablesAccelerometer() {
+        return mCarDockEnablesAccelerometer;
+    }
+
+    boolean isDeskDockEnablesAccelerometer() {
+        return mDeskDockEnablesAccelerometer;
+    }
+
+    public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) {
+        mPersistentVrModeEnabled = persistentVrModeEnabled;
+    }
+
+    public boolean isPersistentVrModeEnabled() {
+        return mPersistentVrModeEnabled;
+    }
+
+    public void setDockMode(int dockMode) {
+        mDockMode = dockMode;
+    }
+
+    public int getDockMode() {
+        return mDockMode;
+    }
+
+    public boolean hasNavigationBar() {
+        return mHasNavigationBar;
+    }
+
+    public boolean navigationBarCanMove() {
+        return mNavigationBarCanMove;
+    }
+
+    public void setLidState(int lidState) {
+        mLidState = lidState;
+    }
+
+    public int getLidState() {
+        return mLidState;
+    }
+
+    public void setAwake(boolean awake) {
+        mAwake = awake;
+    }
+
+    public boolean isAwake() {
+        return mAwake;
+    }
+
+    public boolean isScreenOnEarly() {
+        return mScreenOnEarly;
+    }
+
+    public boolean isScreenOnFully() {
+        return mScreenOnFully;
+    }
+
+    public boolean isKeyguardDrawComplete() {
+        return mKeyguardDrawComplete;
+    }
+
+    public boolean isWindowManagerDrawComplete() {
+        return mWindowManagerDrawComplete;
+    }
+
+    public ScreenOnListener getScreenOnListener() {
+        return mScreenOnListener;
+    }
+
+    public void screenTurnedOn(ScreenOnListener screenOnListener) {
+        synchronized (mLock) {
+            mScreenOnEarly = true;
+            mScreenOnFully = false;
+            mKeyguardDrawComplete = false;
+            mWindowManagerDrawComplete = false;
+            mScreenOnListener = screenOnListener;
+        }
+    }
+
+    public void screenTurnedOff() {
+        synchronized (mLock) {
+            mScreenOnEarly = false;
+            mScreenOnFully = false;
+            mKeyguardDrawComplete = false;
+            mWindowManagerDrawComplete = false;
+            mScreenOnListener = null;
+        }
+    }
+
+    /** Return false if we are not awake yet or we have already informed of this event. */
+    public boolean finishKeyguardDrawn() {
+        synchronized (mLock) {
+            if (!mScreenOnEarly || mKeyguardDrawComplete) {
+                return false;
+            }
+
+            mKeyguardDrawComplete = true;
+            mWindowManagerDrawComplete = false;
+        }
+        return true;
+    }
+
+    /** Return false if screen is not turned on or we did already handle this case earlier. */
+    public boolean finishWindowsDrawn() {
+        synchronized (mLock) {
+            if (!mScreenOnEarly || mWindowManagerDrawComplete) {
+                return false;
+            }
+
+            mWindowManagerDrawComplete = true;
+        }
+        return true;
+    }
+
+    /** Return false if it is not ready to turn on. */
+    public boolean finishScreenTurningOn() {
+        synchronized (mLock) {
+            if (DEBUG_SCREEN_ON) Slog.d(TAG,
+                    "finishScreenTurningOn: mAwake=" + mAwake
+                            + ", mScreenOnEarly=" + mScreenOnEarly
+                            + ", mScreenOnFully=" + mScreenOnFully
+                            + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
+                            + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+
+            if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
+                    || (mAwake && !mKeyguardDrawComplete)) {
+                return false;
+            }
+
+            if (DEBUG_SCREEN_ON) Slog.i(TAG, "Finished screen turning on...");
+            mScreenOnListener = null;
+            mScreenOnFully = true;
+        }
+        return true;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "DisplayPolicy");
+        pw.print(prefix + "  mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer);
+        pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer);
+        pw.print(prefix + "  mDockMode=" + Intent.dockStateToString(mDockMode));
+        pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState));
+        pw.print(prefix + "  mAwake=" + mAwake);
+        pw.print(" mScreenOnEarly=" + mScreenOnEarly);
+        pw.println(" mScreenOnFully=" + mScreenOnFully);
+        pw.print(prefix + "  mKeyguardDrawComplete=" + mKeyguardDrawComplete);
+        pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+        pw.println(prefix + "  mHdmiPlugged=" + mHdmiPlugged);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 2074ecf..685c444 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -16,42 +16,140 @@
 
 package com.android.server.wm;
 
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Surface;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowManagerPolicy.RotationSource;
+import com.android.server.policy.WindowOrientationListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
 
 /**
  * Defines the mapping between orientation and rotation of a display.
+ * Non-public methods are assumed to run inside WM lock.
  */
 public class DisplayRotation {
-    private final DisplayContent mDisplayContent;
-    private final WindowManagerPolicy mPolicy;
-    private final Context mContext;
-    private RotationSource mRotationSource;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
 
-    int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    private final WindowManagerService mService;
+    private final DisplayPolicy mDisplayPolicy;
+    private final Context mContext;
+    private final Object mLock;
+
+    public final boolean isDefaultDisplay;
+    private final boolean mSupportAutoRotation;
+    private final int mLidOpenRotation;
+    private final int mCarDockRotation;
+    private final int mDeskDockRotation;
+    private final int mUndockedHdmiRotation;
+
+    private OrientationListener mOrientationListener;
+    private StatusBarManagerInternal mStatusBarManagerInternal;
+    private SettingsObserver mSettingsObserver;
+
+    // Default display does not rotate, apps that require non-default orientation will have to
+    // have the orientation emulated.
+    private boolean mForceDefaultOrientation;
+
+    private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+    @VisibleForTesting
     int mLandscapeRotation;  // default landscape
+    @VisibleForTesting
     int mSeascapeRotation;   // "other" landscape, 180 degrees from mLandscapeRotation
+    @VisibleForTesting
     int mPortraitRotation;   // default portrait
+    @VisibleForTesting
     int mUpsideDownRotation; // "other" portrait
 
-    DisplayRotation(DisplayContent displayContent, WindowManagerPolicy policy, Context context) {
-        mDisplayContent = displayContent;
-        mPolicy = policy;
-        mContext = context;
+    // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
+    private int mShowRotationSuggestions;
+
+    private int mAllowAllRotations = -1;
+    private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+    private int mUserRotation = Surface.ROTATION_0;
+
+    private int mDemoHdmiRotation;
+    private int mDemoRotation;
+    private boolean mDemoHdmiRotationLock;
+    private boolean mDemoRotationLock;
+
+    DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
+        this(service, displayContent, displayContent.getDisplayPolicy(),
+                service.mContext, service.getWindowManagerLock());
     }
 
-    void configure() {
-        mRotationSource = mPolicy.getRotationSource(mDisplayContent.getDisplayId());
+    @VisibleForTesting
+    DisplayRotation(WindowManagerService service, DisplayContent displayContent,
+            DisplayPolicy displayPolicy, Context context, Object lock) {
+        mService = service;
+        mDisplayPolicy = displayPolicy;
+        mContext = context;
+        mLock = lock;
+        isDefaultDisplay = displayContent.isDefaultDisplay;
 
-        final int width = mDisplayContent.mBaseDisplayWidth;
-        final int height = mDisplayContent.mBaseDisplayHeight;
+        mSupportAutoRotation = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_supportAutoRotation);
+        mLidOpenRotation = readRotation(
+                com.android.internal.R.integer.config_lidOpenRotation);
+        mCarDockRotation = readRotation(
+                com.android.internal.R.integer.config_carDockRotation);
+        mDeskDockRotation = readRotation(
+                com.android.internal.R.integer.config_deskDockRotation);
+        mUndockedHdmiRotation = readRotation(
+                com.android.internal.R.integer.config_undockedHdmiRotation);
+
+        if (isDefaultDisplay) {
+            final Handler uiHandler = UiThread.getHandler();
+            mOrientationListener = new OrientationListener(mContext, uiHandler);
+            mOrientationListener.setCurrentRotation(displayContent.getRotation());
+            mSettingsObserver = new SettingsObserver(uiHandler);
+            mSettingsObserver.observe();
+        }
+    }
+
+    private int readRotation(int resID) {
+        try {
+            final int rotation = mContext.getResources().getInteger(resID);
+            switch (rotation) {
+                case 0:
+                    return Surface.ROTATION_0;
+                case 90:
+                    return Surface.ROTATION_90;
+                case 180:
+                    return Surface.ROTATION_180;
+                case 270:
+                    return Surface.ROTATION_270;
+            }
+        } catch (Resources.NotFoundException e) {
+            // fall through
+        }
+        return -1;
+    }
+
+    void configure(int width, int height, int shortSizeDp, int longSizeDp) {
         final Resources res = mContext.getResources();
         if (width > height) {
             mLandscapeRotation = Surface.ROTATION_0;
@@ -75,7 +173,60 @@
             }
         }
 
-        mPolicy.setInitialDisplaySize(this, width, height, mDisplayContent.mBaseDisplayDensity);
+        // For demo purposes, allow the rotation of the HDMI display to be controlled.
+        // By default, HDMI locks rotation to landscape.
+        if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
+            mDemoHdmiRotation = mPortraitRotation;
+        } else {
+            mDemoHdmiRotation = mLandscapeRotation;
+        }
+        mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
+
+        // For demo purposes, allow the rotation of the remote display to be controlled.
+        // By default, remote display locks rotation to landscape.
+        if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
+            mDemoRotation = mPortraitRotation;
+        } else {
+            mDemoRotation = mLandscapeRotation;
+        }
+        mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
+
+        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
+        // http://developer.android.com/guide/practices/screens_support.html#range
+        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
+        // so if the orientation is forced, we need to respect that no matter what.
+        final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
+        // so if the orientation is forced, we need to respect that no matter what.
+        final boolean isTv = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK);
+        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
+                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
+                // For debug purposes the next line turns this feature off with:
+                // $ adb shell setprop config.override_forced_orient true
+                // $ adb shell wm size reset
+                !"true".equals(SystemProperties.get("config.override_forced_orient"));
+    }
+
+    void setRotation(int rotation) {
+        if (mOrientationListener != null) {
+            mOrientationListener.setCurrentRotation(rotation);
+        }
+    }
+
+    void setCurrentOrientation(int newOrientation) {
+        if (newOrientation != mCurrentAppOrientation) {
+            mCurrentAppOrientation = newOrientation;
+            if (isDefaultDisplay) {
+                updateOrientationListenerLw();
+            }
+        }
+    }
+
+    /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
+    boolean isDefaultOrientationForced() {
+        return mForceDefaultOrientation;
     }
 
     public int getLandscapeRotation() {
@@ -98,31 +249,261 @@
         return mCurrentAppOrientation;
     }
 
-    public int getSensorRotation() {
-        return mRotationSource != null ? mRotationSource.getProposedRotation() : -1;
+    public DisplayPolicy getDisplayPolicy() {
+        return mDisplayPolicy;
     }
 
-    public boolean isDefaultDisplay() {
-        return mDisplayContent.isDefaultDisplay;
+    public WindowOrientationListener getOrientationListener() {
+        return mOrientationListener;
     }
 
-    void setRotation(int rotation) {
-        if (mRotationSource != null) {
-            mRotationSource.setCurrentRotation(rotation);
+    public int getUserRotation() {
+        return mUserRotation;
+    }
+
+    public int getUserRotationMode() {
+        return mUserRotationMode;
+    }
+
+    public void updateOrientationListener() {
+        synchronized (mLock) {
+            updateOrientationListenerLw();
         }
     }
 
-    void setCurrentOrientation(int newOrientation) {
-        if (newOrientation != mCurrentAppOrientation) {
-            mCurrentAppOrientation = newOrientation;
-            // TODO(multi-display): Separate orientation listeners.
-            if (mDisplayContent.isDefaultDisplay) {
-                mPolicy.updateOrientationListener();
+    /**
+     * Various use cases for invoking this function:
+     * <li>Screen turning off, should always disable listeners if already enabled.</li>
+     * <li>Screen turned on and current app has sensor based orientation, enable listeners
+     *     if not already enabled.</li>
+     * <li>Screen turned on and current app does not have sensor orientation, disable listeners
+     *     if already enabled.</li>
+     * <li>Screen turning on and current app has sensor based orientation, enable listeners
+     *     if needed.</li>
+     * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
+     */
+    private void updateOrientationListenerLw() {
+        if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
+            // If sensor is turned off or nonexistent for some reason.
+            return;
+        }
+
+        final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
+        final boolean awake = mDisplayPolicy.isAwake();
+        final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
+        final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
+
+        // Could have been invoked due to screen turning on or off or
+        // change of the currently visible window's orientation.
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
+                + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
+                + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
+                + ", keyguardDrawComplete=" + keyguardDrawComplete
+                + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
+
+        boolean disable = true;
+        // Note: We postpone the rotating of the screen until the keyguard as well as the
+        // window manager have reported a draw complete or the keyguard is going away in dismiss
+        // mode.
+        if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
+            if (needSensorRunning()) {
+                disable = false;
+                // Enable listener if not already enabled.
+                if (!mOrientationListener.mEnabled) {
+                    // Don't clear the current sensor orientation if the keyguard is going away in
+                    // dismiss mode. This allows window manager to use the last sensor reading to
+                    // determine the orientation vs. falling back to the last known orientation if
+                    // the sensor reading was cleared which can cause it to relaunch the app that
+                    // will show in the wrong orientation first before correcting leading to app
+                    // launch delays.
+                    mOrientationListener.enable(true /* clearCurrentRotation */);
+                }
             }
         }
+        // Check if sensors need to be disabled.
+        if (disable && mOrientationListener.mEnabled) {
+            mOrientationListener.disable();
+        }
     }
 
-    public int rotationForOrientation(int orientation, int lastRotation, int preferredRotation) {
+    /**
+     * We always let the sensor be switched on by default except when
+     * the user has explicitly disabled sensor based rotation or when the
+     * screen is switched off.
+     */
+    private boolean needSensorRunning() {
+        if (mSupportAutoRotation) {
+            if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+                // If the application has explicitly requested to follow the
+                // orientation, then we need to turn the sensor on.
+                return true;
+            }
+        }
+
+        final int dockMode = mDisplayPolicy.getDockMode();
+        if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
+                && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
+                || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
+                        && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
+            // Enable accelerometer if we are docked in a dock that enables accelerometer
+            // orientation management.
+            return true;
+        }
+
+        if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+            // If the setting for using the sensor by default is enabled, then
+            // we will always leave it on.  Note that the user could go to
+            // a window that forces an orientation that does not use the
+            // sensor and in theory we could turn it off... however, when next
+            // turning it on we won't have a good value for the current
+            // orientation for a little bit, which can cause orientation
+            // changes to lag, so we'd like to keep it always on.  (It will
+            // still be turned off when the screen is off.)
+
+            // When locked we can provide rotation suggestions users can approve to change the
+            // current screen rotation. To do this the sensor needs to be running.
+            return mSupportAutoRotation &&
+                    mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
+        }
+        return mSupportAutoRotation;
+    }
+
+    /**
+     * Given an orientation constant, returns the appropriate surface rotation,
+     * taking into account sensors, docking mode, rotation lock, and other factors.
+     *
+     * @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) {
+        if (DEBUG_ORIENTATION) {
+            Slog.v(TAG, "rotationForOrientation(orient="
+                        + orientation + ", last=" + lastRotation
+                        + "); user=" + mUserRotation + " "
+                        + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+                            ? "USER_ROTATION_LOCKED" : "")
+                        );
+        }
+
+        if (mForceDefaultOrientation) {
+            return Surface.ROTATION_0;
+        }
+
+        int sensorRotation = mOrientationListener != null
+                ? mOrientationListener.getProposedRotation() // may be -1
+                : -1;
+        if (sensorRotation < 0) {
+            sensorRotation = lastRotation;
+        }
+
+        final int lidState = mDisplayPolicy.getLidState();
+        final int dockMode = mDisplayPolicy.getDockMode();
+        final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+        final boolean carDockEnablesAccelerometer =
+                mDisplayPolicy.isCarDockEnablesAccelerometer();
+        final boolean deskDockEnablesAccelerometer =
+                mDisplayPolicy.isDeskDockEnablesAccelerometer();
+
+        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;
+        } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+            // Ignore sensor when lid switch is open and rotation is forced.
+            preferredRotation = mLidOpenRotation;
+        } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
+                && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
+            // Ignore sensor when in car dock unless explicitly enabled.
+            // This case can override the behavior of NOSENSOR, and can also
+            // enable 180 degree rotation while docked.
+            preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
+        } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+                && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
+            // Ignore sensor when in desk dock unless explicitly enabled.
+            // This case can override the behavior of NOSENSOR, and can also
+            // enable 180 degree rotation while docked.
+            preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
+        } else if (hdmiPlugged && mDemoHdmiRotationLock) {
+            // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
+            // Note that the dock orientation overrides the HDMI orientation.
+            preferredRotation = mDemoHdmiRotation;
+        } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+                && mUndockedHdmiRotation >= 0) {
+            // Ignore sensor when plugged into HDMI and an undocked orientation has
+            // been specified in the configuration (only for legacy devices without
+            // full multi-display support).
+            // Note that the dock orientation overrides the HDMI orientation.
+            preferredRotation = mUndockedHdmiRotation;
+        } else if (mDemoRotationLock) {
+            // Ignore sensor when demo rotation lock is enabled.
+            // Note that the dock orientation and HDMI rotation lock override this.
+            preferredRotation = mDemoRotation;
+        } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+            // While in VR, apps always prefer a portrait rotation. This does not change
+            // any apps that explicitly set landscape, but does cause sensors be ignored,
+            // and ignored any orientation lock that the user has set (this conditional
+            // should remain above the ORIENTATION_LOCKED conditional below).
+            preferredRotation = mPortraitRotation;
+        } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            // Application just wants to remain locked in the last rotation.
+            preferredRotation = lastRotation;
+        } else if (!mSupportAutoRotation) {
+            // If we don't support auto-rotation then bail out here and ignore
+            // the sensor and any rotation lock settings.
+            preferredRotation = -1;
+        } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+                        && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
+            // Otherwise, use sensor only if requested by the application or enabled
+            // by default for USER or UNSPECIFIED modes.  Does not apply to NOSENSOR.
+            if (mAllowAllRotations < 0) {
+                // Can't read this during init() because the context doesn't
+                // have display metrics at that time so we cannot determine
+                // tablet vs. phone then.
+                mAllowAllRotations = mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
+            }
+            if (sensorRotation != Surface.ROTATION_180
+                    || mAllowAllRotations == 1
+                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+                preferredRotation = sensorRotation;
+            } else {
+                preferredRotation = lastRotation;
+            }
+        } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+                && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+            // Apply rotation lock.  Does not apply to NOSENSOR.
+            // The idea is that the user rotation expresses a weak preference for the direction
+            // of gravity and as NOSENSOR is never affected by gravity, then neither should
+            // NOSENSOR be affected by rotation lock (although it will be affected by docks).
+            preferredRotation = mUserRotation;
+        } else {
+            // No overriding preference.
+            // We will do exactly what the application asked us to do.
+            preferredRotation = -1;
+        }
+
         switch (orientation) {
             case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                 // Return portrait unless overridden.
@@ -184,6 +565,14 @@
         }
     }
 
+    private boolean isLandscapeOrSeascape(int rotation) {
+        return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
+    }
+
+    private boolean isAnyPortrait(int rotation) {
+        return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
+    }
+
     /**
      * Given an orientation constant and a rotation, returns true if the rotation
      * has compatible metrics to the requested orientation.  For example, if
@@ -214,7 +603,7 @@
         }
     }
 
-    public boolean isValidRotationChoice(final int preferredRotation) {
+    private 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:
@@ -240,12 +629,143 @@
         return false;
     }
 
-    private boolean isLandscapeOrSeascape(int rotation) {
-        return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
+    private boolean isRotationChoicePossible(int orientation) {
+        // Rotation choice is only shown when the user is in locked mode.
+        if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+        // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+        // demo, hdmi, vr, etc mode.
+
+        // Determine if the rotation is currently forced.
+        if (mForceDefaultOrientation) {
+            return false; // Rotation is forced to default orientation.
+        }
+
+        final int lidState = mDisplayPolicy.getLidState();
+        if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+            return false; // Rotation is forced mLidOpenRotation.
+        }
+
+        final int dockMode = mDisplayPolicy.getDockMode();
+        final boolean carDockEnablesAccelerometer = false;
+        if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
+            return false; // Rotation forced to mCarDockRotation.
+        }
+
+        final boolean deskDockEnablesAccelerometer =
+                mDisplayPolicy.isDeskDockEnablesAccelerometer();
+        if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+                && !deskDockEnablesAccelerometer) {
+            return false; // Rotation forced to mDeskDockRotation.
+        }
+
+        final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+        if (hdmiPlugged && mDemoHdmiRotationLock) {
+            return false; // Rotation forced to mDemoHdmiRotation.
+
+        } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+                && mUndockedHdmiRotation >= 0) {
+            return false; // Rotation forced to mUndockedHdmiRotation.
+
+        } else if (mDemoRotationLock) {
+            return false; // Rotation forced to mDemoRotation.
+
+        } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+            return false; // Rotation forced to mPortraitRotation.
+
+        } else if (!mSupportAutoRotation) {
+            return false;
+        }
+
+        // Ensure that some rotation choice is possible for the given orientation.
+        switch (orientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // NOSENSOR description is ambiguous, in reality WM ignores user choice.
+                return true;
+        }
+
+        // Rotation is forced, should be controlled by system.
+        return false;
     }
 
-    private boolean isAnyPortrait(int rotation) {
-        return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
+    /** Notify the StatusBar that system rotation suggestion has changed. */
+    private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
+        if (mStatusBarManagerInternal == null) {
+            mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
+        }
+        if (mStatusBarManagerInternal != null) {
+            mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
+        }
+    }
+
+    private static String allowAllRotationsToString(int allowAll) {
+        switch (allowAll) {
+            case -1:
+                return "unknown";
+            case 0:
+                return "false";
+            case 1:
+                return "true";
+            default:
+                return Integer.toString(allowAll);
+        }
+    }
+
+    public void onUserSwitch() {
+        if (mSettingsObserver != null) {
+            mSettingsObserver.onChange(false);
+        }
+    }
+
+    /** Return whether the rotation settings has changed. */
+    private boolean updateSettings() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        boolean shouldUpdateRotation = false;
+
+        synchronized (mLock) {
+            boolean shouldUpdateOrientationListener = false;
+
+            // Configure rotation suggestions.
+            final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+                    UserHandle.USER_CURRENT);
+            if (mShowRotationSuggestions != showRotationSuggestions) {
+                mShowRotationSuggestions = showRotationSuggestions;
+                shouldUpdateOrientationListener = true;
+            }
+
+            // Configure rotation lock.
+            final int userRotation = Settings.System.getIntForUser(resolver,
+                    Settings.System.USER_ROTATION, Surface.ROTATION_0,
+                    UserHandle.USER_CURRENT);
+            if (mUserRotation != userRotation) {
+                mUserRotation = userRotation;
+                shouldUpdateRotation = true;
+            }
+
+            final int userRotationMode = Settings.System.getIntForUser(resolver,
+                    Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
+                            ? WindowManagerPolicy.USER_ROTATION_FREE
+                            : WindowManagerPolicy.USER_ROTATION_LOCKED;
+            if (mUserRotationMode != userRotationMode) {
+                mUserRotationMode = userRotationMode;
+                shouldUpdateOrientationListener = true;
+                shouldUpdateRotation = true;
+            }
+
+            if (shouldUpdateOrientationListener) {
+                updateOrientationListenerLw(); // Enable or disable the orientation listener.
+            }
+        }
+
+        return shouldUpdateRotation;
     }
 
     void dump(String prefix, PrintWriter pw) {
@@ -256,5 +776,106 @@
         pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
         pw.print(prefix + "  mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
         pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
+
+        pw.print(prefix + "  mSupportAutoRotation=" + mSupportAutoRotation);
+        if (mOrientationListener != null) {
+            pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled);
+        }
+        pw.println();
+
+        pw.print(prefix + "  mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
+        pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
+        pw.print(prefix + "  mUserRotationMode="
+                + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
+        pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
+        pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
+
+        pw.print(prefix + "  mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
+        pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
+        pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
+        pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+    }
+
+    private class OrientationListener extends WindowOrientationListener {
+        final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
+        boolean mEnabled;
+
+        OrientationListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        private class UpdateRunnable implements Runnable {
+            final int mRotation;
+
+            UpdateRunnable(int rotation) {
+                mRotation = rotation;
+            }
+
+            @Override
+            public void run() {
+                // Send interaction hint to improve redraw performance.
+                mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
+                if (isRotationChoicePossible(mCurrentAppOrientation)) {
+                    final boolean isValid = isValidRotationChoice(mRotation);
+                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
+                } else {
+                    mService.updateRotation(false /* alwaysSendConfiguration */,
+                            false /* forceRelayout */);
+                }
+            }
+        }
+
+        @Override
+        public void onProposedRotationChanged(int rotation) {
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
+            Runnable r = mRunnableCache.get(rotation, null);
+            if (r == null) {
+                r = new UpdateRunnable(rotation);
+                mRunnableCache.put(rotation, r);
+            }
+            getHandler().post(r);
+        }
+
+        @Override
+        public void enable(boolean clearCurrentRotation) {
+            super.enable(clearCurrentRotation);
+            mEnabled = true;
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
+        }
+
+        @Override
+        public void disable() {
+            super.disable();
+            mEnabled = false;
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void observe() {
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.ACCELEROMETER_ROTATION), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.USER_ROTATION), false, this,
+                    UserHandle.USER_ALL);
+            updateSettings();
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (updateSettings()) {
+                mService.updateRotation(true /* alwaysSendConfiguration */,
+                        false /* forceRelayout */);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a2b3783..6578885 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2480,7 +2480,7 @@
                 dc.setLastOrientation(req);
                 //send a message to Policy indicating orientation change to take
                 //action like disabling/enabling sensors etc.,
-                dc.mDisplayRotation.setCurrentOrientation(req);
+                dc.getDisplayRotation().setCurrentOrientation(req);
                 return dc.updateRotationUnchecked(forceUpdate);
             }
             return false;
@@ -3817,28 +3817,31 @@
         long origId = Binder.clearCallingIdentity();
 
         try {
-            // TODO(multi-display): Update rotation for different displays separately.
-            final boolean rotationChanged;
-            final int displayId;
             synchronized (mWindowMap) {
-                final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
-                rotationChanged = displayContent.updateRotationUnchecked();
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                if (!rotationChanged || forceRelayout) {
-                    displayContent.setLayoutNeeded();
+                boolean layoutNeeded = false;
+                final int displayCount = mRoot.mChildren.size();
+                for (int i = 0; i < displayCount; ++i) {
+                    final DisplayContent displayContent = mRoot.mChildren.get(i);
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
+                    final boolean rotationChanged = displayContent.updateRotationUnchecked();
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+
+                    if (!rotationChanged || forceRelayout) {
+                        displayContent.setLayoutNeeded();
+                        layoutNeeded = true;
+                    }
+                    if (rotationChanged || alwaysSendConfiguration) {
+                        mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+                                .sendToTarget();
+                    }
+                }
+
+                if (layoutNeeded) {
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                             "updateRotation: performSurfacePlacement");
                     mWindowPlacerLocked.performSurfacePlacement();
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
-                displayId = displayContent.getDisplayId();
-            }
-
-            if (rotationChanged || alwaysSendConfiguration) {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
-                sendNewConfiguration(displayId);
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5775,7 +5778,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mPolicy.isDefaultOrientationForced(), isSecure,
+                    displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -7126,11 +7129,7 @@
             }
             finishSeamlessRotation();
 
-            final DisplayContent displayContent = w.getDisplayContent();
-            if (displayContent.updateRotationUnchecked()) {
-                mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                        .sendToTarget();
-            }
+            w.getDisplayContent().updateRotationAndSendNewConfigIfNeeded();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 46fcae7..ef87f9d 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -55,7 +55,7 @@
 
 import com.android.server.policy.keyguard.KeyguardServiceDelegate;
 import com.android.server.wm.DisplayFrames;
-import com.android.server.wm.TestDisplayContent;
+import com.android.server.wm.WindowTestUtils.TestDisplayContent;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Before;
@@ -124,7 +124,6 @@
         mNavigationBar.attrs.gravity = Gravity.BOTTOM;
 
         mPolicy.addWindow(mNavigationBar);
-        mPolicy.mHasNavigationBar = true;
         mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
     }
 
@@ -246,8 +245,10 @@
                 policy[0].mAccessibilityManager = new AccessibilityManager(context,
                         mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
                 policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
-                policy[0].mNavigationBarCanMove = true;
-                policy[0].onConfigurationChanged(TestDisplayContent.create(policy[0], context));
+
+                final TestDisplayContent displayContent = TestDisplayContent.create(context);
+                policy[0].setDefaultDisplay(displayContent);
+                policy[0].onConfigurationChanged(displayContent);
             });
             return policy[0];
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index f6599dc..e8d8022 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -42,6 +42,10 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
 /**
  * Tests for the {@link AppWindowToken} class.
  *
@@ -162,6 +166,9 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
+        final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
+        mDisplayContent.setDisplayRotation(spiedRotation);
+
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
@@ -169,20 +176,21 @@
         mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        performRotation(Surface.ROTATION_90);
+        performRotation(spiedRotation, Surface.ROTATION_90);
         appWindow.resizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
-        performRotation(Surface.ROTATION_270);
+        performRotation(spiedRotation, Surface.ROTATION_270);
         assertTrue(appWindow.resizeReported);
+
         appWindow.removeImmediately();
     }
 
-    private void performRotation(int rotationToReport) {
-        ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = rotationToReport;
+    private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
+        doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
         sWm.updateRotation(false, false);
-        // Simulate animator finishing orientation change
-        sWm.mRoot.mOrientationChangeComplete = true;
+        // Prevent the next rotation from being deferred by animation.
+        sWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/servicestests/src/com/android/server/wm/TestDisplayContent.java
deleted file mode 100644
index 09c6a07..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/TestDisplayContent.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.view.Display;
-import android.view.Surface;
-
-import com.android.server.policy.WindowManagerPolicy;
-
-public class TestDisplayContent extends DisplayContent {
-
-    TestDisplayContent(Display display, WindowManagerService service,
-            WallpaperController wallpaperController, DisplayWindowController controller) {
-        super(display, service, wallpaperController, controller);
-    }
-
-    public static TestDisplayContent create(WindowManagerPolicy policy, Context context) {
-        final TestDisplayContent displayContent = mock(TestDisplayContent.class);
-        displayContent.isDefaultDisplay = true;
-
-        final DisplayRotation displayRotation = new DisplayRotation(
-                displayContent, policy, context);
-        displayRotation.mPortraitRotation = Surface.ROTATION_0;
-        displayRotation.mLandscapeRotation = Surface.ROTATION_90;
-        displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
-        displayRotation.mSeascapeRotation = Surface.ROTATION_270;
-
-        when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
-
-        return displayContent;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index e8f375d..735e284 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -72,15 +72,7 @@
 
     }
 
-    @Override
-    public boolean isDefaultOrientationForced() {
-        return false;
-    }
-
-    @Override
-    public void setInitialDisplaySize(DisplayRotation displayRotation, int width, int height,
-            int density) {
-
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
     }
 
     @Override
@@ -389,13 +381,6 @@
     public void onKeyguardOccludedChangedLw(boolean occluded) {
     }
 
-    @Override
-    public int rotationForOrientationLw(DisplayRotation displayRotation, int orientation,
-            int lastRotation) {
-        return rotationToReport;
-    }
-
-    @Override
     public void setSafeMode(boolean safeMode) {
 
     }
@@ -593,13 +578,4 @@
     public boolean setAodShowing(boolean aodShowing) {
         return false;
     }
-
-    @Override
-    public void updateOrientationListener() {
-    }
-
-    @Override
-    public RotationSource getRotationSource(int displayId) {
-        return null;
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index a48a3b9..a1b1640 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -22,8 +22,10 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.view.Display;
 import android.view.IApplicationToken;
 import android.view.IWindow;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
@@ -60,6 +62,37 @@
         return service;
     }
 
+    /** An extension of {@link DisplayContent} to gain package scoped access. */
+    public static class TestDisplayContent extends DisplayContent {
+
+        private TestDisplayContent(Display display, WindowManagerService service,
+                WallpaperController wallpaperController, DisplayWindowController controller) {
+            super(display, service, wallpaperController, controller);
+        }
+
+        /** Create a mocked default {@link DisplayContent}. */
+        public static TestDisplayContent create(Context context) {
+            final TestDisplayContent displayContent = mock(TestDisplayContent.class);
+            displayContent.isDefaultDisplay = true;
+
+            final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+            when(displayPolicy.navigationBarCanMove()).thenReturn(true);
+            when(displayPolicy.hasNavigationBar()).thenReturn(true);
+
+            final DisplayRotation displayRotation = new DisplayRotation(
+                    mock(WindowManagerService.class), displayContent, displayPolicy,
+                    context, new Object());
+            displayRotation.mPortraitRotation = Surface.ROTATION_0;
+            displayRotation.mLandscapeRotation = Surface.ROTATION_90;
+            displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
+            displayRotation.mSeascapeRotation = Surface.ROTATION_270;
+
+            when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
+
+            return displayContent;
+        }
+    }
+
     /**
      * Creates a mock instance of {@link StackWindowController}.
      */