Support forced display properties on secondary display

- Apply config_maxUiWidth to all displays.
- Forced size, density, scaling mode for non-default display are
  stored in DisplaySettings. The settings will apply when creating
  DisplayContent.
- Simplify WMS.displayReady because display policy would be updated
  via hierarchy of configuration container when config change happens
  from reconfigure.
- Remove WMS.displayReady(int) because the same things are done during
  initializing DisplayContent.
- Remove WMS.onDisplayAdded from Supervisor because during creating
  ActivityDisplay, DisplayWindowController, the DisplayContent will
  also be initialized. Without removing this, it may lost the settings
  that has been applied by configuring from the initial info again.
- Remove WMS.onDisplayRemoved from Supervisor because
  DisplayWindowController has handled it when removing container.

Bug: b/111362047
Test: atest DisplayContentTests
Test: atest DisplaySettingsTests
Test: atest ActivityManagerMultiDisplayTests

Change-Id: I5ae6f83db4c1264a32b7ce6a6951d3ba7e7fb662
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index ede13ef..a72470d 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -163,6 +163,23 @@
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
     }
 
+    void onDisplayChanged() {
+        // The window policy is responsible for stopping activities on the default display.
+        final int displayId = mDisplay.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            final int displayState = mDisplay.getState();
+            if (displayState == Display.STATE_OFF && mOffToken == null) {
+                mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+            } else if (displayState == Display.STATE_ON && mOffToken != null) {
+                mOffToken.release();
+                mOffToken = null;
+            }
+        }
+
+        updateBounds();
+        mWindowContainerController.onDisplayChanged();
+    }
+
     void addChild(ActivityStack stack, int position) {
         if (position == POSITION_BOTTOM) {
             position = 0;
@@ -1021,6 +1038,12 @@
         releaseSelfIfNeeded();
 
         mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+
+        if (!mAllSleepTokens.isEmpty()) {
+            mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+            mAllSleepTokens.clear();
+            mSupervisor.mService.updateSleepIfNeededLocked();
+        }
     }
 
     private void releaseSelfIfNeeded() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 257a004..17454b4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -226,9 +226,6 @@
     static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
     static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
     static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
-    static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
-    static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
-    static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
     static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
     static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
     static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
@@ -675,7 +672,7 @@
         setWindowContainerController(new RootWindowContainerController(this));
 
         mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(this, null);
+        mDisplayManager.registerDisplayListener(this, mHandler);
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
         final Display[] displays = mDisplayManager.getDisplays();
@@ -4108,25 +4105,37 @@
     @Override
     public void onDisplayAdded(int displayId) {
         if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
+        synchronized (mService.mGlobalLock) {
+            getActivityDisplayOrCreateLocked(displayId);
+            mService.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
+        }
     }
 
     @Override
     public void onDisplayRemoved(int displayId) {
         if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can't remove the primary display.");
+        }
+
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay == null) {
+                return;
+            }
+
+            activityDisplay.remove();
+        }
     }
 
     @Override
     public void onDisplayChanged(int displayId) {
         if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
-    }
-
-    private void handleDisplayAdded(int displayId) {
         synchronized (mService.mGlobalLock) {
-            getActivityDisplayOrCreateLocked(displayId);
-            mService.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay != null) {
+                activityDisplay.onDisplayChanged();
+            }
         }
     }
 
@@ -4173,7 +4182,6 @@
         // The display hasn't been added to ActivityManager yet, create a new record now.
         activityDisplay = new ActivityDisplay(this, display);
         addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
-        mWindowManager.onDisplayAdded(displayId);
         return activityDisplay;
     }
 
@@ -4199,47 +4207,6 @@
         mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
     }
 
-    private void handleDisplayRemoved(int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can't remove the primary display.");
-        }
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay == null) {
-                return;
-            }
-
-            activityDisplay.remove();
-
-            releaseSleepTokens(activityDisplay);
-        }
-    }
-
-    private void handleDisplayChanged(int displayId) {
-        synchronized (mService.mGlobalLock) {
-            ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            // TODO: The following code block should be moved into {@link ActivityDisplay}.
-            if (activityDisplay != null) {
-                // The window policy is responsible for stopping activities on the default display
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    int displayState = activityDisplay.mDisplay.getState();
-                    if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
-                        activityDisplay.mOffToken =
-                                mService.acquireSleepToken("Display-off", displayId);
-                    } else if (displayState == Display.STATE_ON
-                            && activityDisplay.mOffToken != null) {
-                        activityDisplay.mOffToken.release();
-                        activityDisplay.mOffToken = null;
-                    }
-                }
-
-                activityDisplay.updateBounds();
-            }
-            mWindowManager.onDisplayChanged(displayId);
-        }
-    }
-
     SleepToken createSleepTokenLocked(String tag, int displayId) {
         final ActivityDisplay display = getActivityDisplay(displayId);
         if (display == null) {
@@ -4264,18 +4231,6 @@
         }
     }
 
-    private void releaseSleepTokens(ActivityDisplay display) {
-        if (display.mAllSleepTokens.isEmpty()) {
-            return;
-        }
-        for (SleepToken token : display.mAllSleepTokens) {
-            mSleepTokens.remove(token);
-        }
-        display.mAllSleepTokens.clear();
-
-        mService.updateSleepIfNeededLocked();
-    }
-
     private StackInfo getStackInfo(ActivityStack stack) {
         final int displayId = stack.mDisplayId;
         final ActivityDisplay display = getActivityDisplay(displayId);
@@ -4597,15 +4552,6 @@
                         }
                     }
                 } break;
-                case HANDLE_DISPLAY_ADDED: {
-                    handleDisplayAdded(msg.arg1);
-                } break;
-                case HANDLE_DISPLAY_CHANGED: {
-                    handleDisplayChanged(msg.arg1);
-                } break;
-                case HANDLE_DISPLAY_REMOVED: {
-                    handleDisplayRemoved(msg.arg1);
-                } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
                     synchronized (mService.mGlobalLock) {
                         ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ca23360..fa9ae52 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -120,6 +120,7 @@
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -137,6 +138,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Slog;
@@ -162,6 +164,8 @@
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -183,6 +187,18 @@
         implements WindowManagerPolicy.DisplayContentInfo {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
 
+    /** The default scaling mode that scales content automatically. */
+    static final int FORCE_SCALING_MODE_AUTO = 0;
+    /** For {@link #setForcedScalingMode} to apply flag {@link Display#FLAG_SCALING_DISABLED}. */
+    static final int FORCE_SCALING_MODE_DISABLED = 1;
+
+    @IntDef(prefix = { "FORCE_SCALING_MODE_" }, value = {
+            FORCE_SCALING_MODE_AUTO,
+            FORCE_SCALING_MODE_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ForceScalingMode {}
+
     /** Unique identifier of this stack. */
     private final int mDisplayId;
 
@@ -237,6 +253,11 @@
      * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
      */
     int mBaseDisplayDensity = 0;
+
+    /**
+     * Whether to disable display scaling. This can be set via shell command "adb shell wm scaling".
+     * @see WindowManagerService#setForcedDisplayScalingMode(int, int)
+     */
     boolean mDisplayScalingDisabled;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
@@ -836,6 +857,7 @@
         // {@link DisplayContent} ready for use.
         mDisplayReady = true;
 
+        mService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
 
         if (mService.mInputManager != null) {
@@ -1790,7 +1812,7 @@
 
         // The display size information is heavily dependent on the resources in the current
         // configuration, so we need to reconfigure it every time the configuration changes.
-        // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
+        // See {@link #configureDisplayPolicy}...sigh...
         mService.reconfigureDisplayLocked(this);
 
         final DockedStackDividerController dividerController = getDockedDividerController();
@@ -2027,6 +2049,68 @@
         updateBounds();
     }
 
+    /**
+     * Forces this display to use the specified density.
+     *
+     * @param density The density in DPI to use. If the value equals to initial density, the setting
+     *                will be cleared.
+     * @param userId The target user to apply. Only meaningful when this is default display. If the
+     *               user id is {@link UserHandle#USER_CURRENT}, it means to apply current settings
+     *               so only need to configure display.
+     */
+    void setForcedDensity(int density, int userId) {
+        final boolean clear = density == mInitialDisplayDensity;
+        final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
+        if (mService.mCurrentUserId == userId || updateCurrent) {
+            mBaseDisplayDensity = density;
+            mService.reconfigureDisplayLocked(this);
+        }
+        if (updateCurrent) {
+            // We are applying existing settings so no need to save it again.
+            return;
+        }
+
+        if (density == mInitialDisplayDensity) {
+            density = 0;
+        }
+        mService.mDisplaySettings.setForcedDensity(this, density, userId);
+    }
+
+    /** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
+    void setForcedScalingMode(@ForceScalingMode int mode) {
+        if (mode != FORCE_SCALING_MODE_DISABLED) {
+            mode = FORCE_SCALING_MODE_AUTO;
+        }
+
+        mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO);
+        Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto"));
+        mService.reconfigureDisplayLocked(this);
+
+        mService.mDisplaySettings.setForcedScalingMode(this, mode);
+    }
+
+    /** If the given width and height equal to initial size, the setting will be cleared. */
+    void setForcedSize(int width, int height) {
+        final boolean clear = mInitialDisplayWidth == width && mInitialDisplayHeight == height;
+        if (!clear) {
+            // Set some sort of reasonable bounds on the size of the display that we will try
+            // to emulate.
+            final int minSize = 200;
+            final int maxScale = 2;
+            width = Math.min(Math.max(width, minSize), mInitialDisplayWidth * maxScale);
+            height = Math.min(Math.max(height, minSize), mInitialDisplayHeight * maxScale);
+        }
+
+        Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
+        updateBaseDisplayMetrics(width, height, mBaseDisplayDensity);
+        mService.reconfigureDisplayLocked(this);
+
+        if (clear) {
+            width = height = 0;
+        }
+        mService.mDisplaySettings.setForcedSize(this, width, height);
+    }
+
     void getStableRect(Rect out) {
         out.set(mDisplayFrames.mStable);
     }
@@ -2225,7 +2309,7 @@
         }
 
         mInputMonitor.onRemoved();
-        mService.onDisplayRemoved(mDisplayId);
+        mService.mWindowPlacerLocked.requestTraversal();
     }
 
     /** Returns true if a removal action is still being deferred. */
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 847cff9..9f98dc5 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -269,7 +269,6 @@
         if (changed) {
             mService.updateRotation(true /* alwaysSendConfiguration */,
                     false /* forceRelayout */);
-            mService.mDisplaySettings.writeSettingsLocked();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 28dc008..44956ab 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -16,12 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
+import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.WindowConfiguration;
-import android.graphics.Rect;
 import android.os.Environment;
+import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -33,6 +35,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.DisplayContent.ForceScalingMode;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -65,17 +68,24 @@
         private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
         private int mUserRotation = Surface.ROTATION_0;
+        private int mForcedWidth;
+        private int mForcedHeight;
+        private int mForcedDensity;
+        private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO;
 
         private Entry(String _name) {
             mName = _name;
         }
 
+        /** @return {@code true} if all values are default. */
         private boolean isEmpty() {
             return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
                     && mOverscanBottom == 0
                     && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
                     && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                    && mUserRotation == Surface.ROTATION_0;
+                    && mUserRotation == Surface.ROTATION_0
+                    && mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
+                    && mForcedScalingMode == FORCE_SCALING_MODE_AUTO;
         }
     }
 
@@ -87,64 +97,84 @@
     DisplaySettings(WindowManagerService service, File folder) {
         mService = service;
         mFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
+        readSettings();
     }
 
-    private Entry getEntry(String name, String uniqueId) {
+    private Entry getEntry(DisplayInfo displayInfo) {
         // Try to get the entry with the unique if possible.
         // Else, fall back on the display name.
         Entry entry;
-        if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
-            entry = mEntries.get(name);
+        if (displayInfo.uniqueId == null || (entry = mEntries.get(displayInfo.uniqueId)) == null) {
+            entry = mEntries.get(displayInfo.name);
         }
         return entry;
     }
 
-    private Entry getOrCreateEntry(String uniqueId, String name) {
-        Entry entry = getEntry(uniqueId, name);
-        if (entry == null) {
-            entry = new Entry(uniqueId);
-            mEntries.put(uniqueId, entry);
-        }
-        return entry;
+    private Entry getOrCreateEntry(DisplayInfo displayInfo) {
+        final Entry entry = getEntry(displayInfo);
+        return entry != null ? entry : new Entry(displayInfo.uniqueId);
     }
 
-    private void removeEntryIfEmpty(String uniqueId, String name) {
-        final Entry entry = getEntry(uniqueId, name);
-        if (entry.isEmpty()) {
-            mEntries.remove(uniqueId);
-            mEntries.remove(name);
-        }
-    }
-
-    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
-        final Entry entry = getEntry(name, uniqueId);
-        if (entry != null) {
-            outRect.left = entry.mOverscanLeft;
-            outRect.top = entry.mOverscanTop;
-            outRect.right = entry.mOverscanRight;
-            outRect.bottom = entry.mOverscanBottom;
-        } else {
-            outRect.set(0, 0, 0, 0);
-        }
-    }
-
-    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
-            int bottom) {
-        Entry entry = mEntries.get(uniqueId);
-        if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) {
-            // All default value, no action needed.
-            return;
-        }
-        entry = getOrCreateEntry(uniqueId, name);
+    void setOverscanLocked(DisplayInfo displayInfo, int left, int top, int right, int bottom) {
+        final Entry entry = getOrCreateEntry(displayInfo);
         entry.mOverscanLeft = left;
         entry.mOverscanTop = top;
         entry.mOverscanRight = right;
         entry.mOverscanBottom = bottom;
-        removeEntryIfEmpty(uniqueId, name);
+        writeSettingsIfNeeded(entry, displayInfo);
     }
 
-    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
-        final Entry entry = getEntry(name, uniqueId);
+    void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mUserRotationMode = rotationMode;
+        entry.mUserRotation = rotation;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedSize(DisplayContent displayContent, int width, int height) {
+        if (displayContent.isDefaultDisplay) {
+            final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
+            Settings.Global.putString(mService.mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SIZE_FORCED, sizeString);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedWidth = width;
+        entry.mForcedHeight = height;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedDensity(DisplayContent displayContent, int density, int userId) {
+        if (displayContent.isDefaultDisplay) {
+            final String densityString = density == 0 ? "" : Integer.toString(density);
+            Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedDensity = density;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
+        if (displayContent.isDefaultDisplay) {
+            Settings.Global.putInt(mService.mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SCALING_FORCE, mode);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedScalingMode = mode;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    private int getWindowingModeLocked(Entry entry, int displayId) {
         int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         // This display used to be in freeform, but we don't support freeform anymore, so fall
@@ -168,54 +198,35 @@
         return windowingMode;
     }
 
-    void setUserRotation(DisplayContent dc, int rotationMode, int rotation) {
+    void applySettingsToDisplayLocked(DisplayContent dc) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
+        final Entry entry = getEntry(displayInfo);
 
-        final String uniqueId = displayInfo.uniqueId;
-        final String name = displayInfo.name;
-        Entry entry = getEntry(displayInfo.name, uniqueId);
-        if (rotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                && rotation == Surface.ROTATION_0 && entry == null) {
-            // All default values. No action needed.
+        // Setting windowing mode first, because it may override overscan values later.
+        dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
+
+        if (entry == null) {
             return;
         }
 
-        entry = getOrCreateEntry(uniqueId, name);
-        entry.mUserRotationMode = rotationMode;
-        entry.mUserRotation = rotation;
-        removeEntryIfEmpty(uniqueId, name);
+        displayInfo.overscanLeft = entry.mOverscanLeft;
+        displayInfo.overscanTop = entry.mOverscanTop;
+        displayInfo.overscanRight = entry.mOverscanRight;
+        displayInfo.overscanBottom = entry.mOverscanBottom;
+
+        dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+
+        if (entry.mForcedDensity != 0) {
+            dc.mBaseDisplayDensity = entry.mForcedDensity;
+        }
+        if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
+            dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
+                    dc.mBaseDisplayDensity);
+        }
+        dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
     }
 
-    private void restoreUserRotation(DisplayContent dc) {
-        final DisplayInfo info = dc.getDisplayInfo();
-
-        final Entry entry = getEntry(info.name, info.uniqueId);
-        final int userRotationMode = entry != null ? entry.mUserRotationMode
-                : WindowManagerPolicy.USER_ROTATION_FREE;
-        final int userRotation = entry != null ? entry.mUserRotation
-                : Surface.ROTATION_0;
-
-        dc.getDisplayRotation().restoreUserRotation(userRotationMode, userRotation);
-    }
-
-    void applySettingsToDisplayLocked(DisplayContent dc) {
-        final DisplayInfo displayInfo = dc.getDisplayInfo();
-
-        // Setting windowing mode first, because it may override overscan values later.
-        dc.setWindowingMode(getWindowingModeLocked(displayInfo.name, displayInfo.uniqueId,
-                dc.getDisplayId()));
-
-        final Rect rect = new Rect();
-        getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
-        displayInfo.overscanLeft = rect.left;
-        displayInfo.overscanTop = rect.top;
-        displayInfo.overscanRight = rect.right;
-        displayInfo.overscanBottom = rect.bottom;
-
-        restoreUserRotation(dc);
-    }
-
-    void readSettingsLocked() {
+    private void readSettings() {
         FileInputStream stream;
         try {
             stream = mFile.openRead();
@@ -306,12 +317,29 @@
                     WindowManagerPolicy.USER_ROTATION_FREE);
             entry.mUserRotation = getIntAttribute(parser, "userRotation",
                     Surface.ROTATION_0);
+            entry.mForcedWidth = getIntAttribute(parser, "forcedWidth");
+            entry.mForcedHeight = getIntAttribute(parser, "forcedHeight");
+            entry.mForcedDensity = getIntAttribute(parser, "forcedDensity");
+            entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode",
+                    FORCE_SCALING_MODE_AUTO);
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
     }
 
-    void writeSettingsLocked() {
+    private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) {
+        if (changedEntry.isEmpty()) {
+            boolean removed = mEntries.remove(displayInfo.uniqueId) != null;
+            // Legacy name might have been in used, so we need to clear it.
+            removed |= mEntries.remove(displayInfo.name) != null;
+            if (!removed) {
+                // The entry didn't exist so nothing is changed and no need to update the file.
+                return;
+            }
+        } else {
+            mEntries.put(displayInfo.uniqueId, changedEntry);
+        }
+
         FileOutputStream stream;
         try {
             stream = mFile.startWrite();
@@ -351,6 +379,17 @@
                 if (entry.mUserRotation != Surface.ROTATION_0) {
                     out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
                 }
+                if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
+                    out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth));
+                    out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight));
+                }
+                if (entry.mForcedDensity != 0) {
+                    out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity));
+                }
+                if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) {
+                    out.attribute(null, "forcedScalingMode",
+                            Integer.toString(entry.mForcedScalingMode));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index ab87759..3282b1c 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -81,6 +81,22 @@
     }
 
     /**
+     * Called when the corresponding display receives
+     * {@link android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged(int)}.
+     */
+    public void onDisplayChanged() {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                if (DEBUG_DISPLAY) Slog.i(TAG_WM, "onDisplayChanged: could not find display="
+                        + mDisplayId);
+                return;
+            }
+            mContainer.updateDisplayInfo();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
+    /**
      * Positions the task stack at the given position in the task stack container.
      */
     public void positionChildAt(StackWindowController child, int position,
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ad0b8ec..bc0f19a 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -105,9 +105,6 @@
         // Create the DisplayContentsAnimator object by retrieving it if the associated
         // {@link DisplayContent} exists.
         getDisplayContentsAnimatorLocked(displayId);
-        if (displayId == DEFAULT_DISPLAY) {
-            mInitialized = true;
-        }
     }
 
     void removeDisplayLocked(final int displayId) {
@@ -122,6 +119,10 @@
         mDisplayContentsAnimators.delete(displayId);
     }
 
+    void ready() {
+        mInitialized = true;
+    }
+
     /**
      * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
      * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5642b1f..3aad73c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -930,7 +930,6 @@
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings(this);
-        mDisplaySettings.readSettingsLocked();
 
         mPolicy = policy;
         mAnimator = new WindowAnimator(this);
@@ -3241,7 +3240,7 @@
                 final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId);
                 final int targetDensity = forcedDensity != 0 ? forcedDensity
                         : displayContent.mInitialDisplayDensity;
-                setForcedDisplayDensityLocked(displayContent, targetDensity);
+                displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT);
             }
         }
     }
@@ -4415,31 +4414,15 @@
     }
 
     public void displayReady() {
-        final int displayCount = mRoot.mChildren.size();
-        for (int i = 0; i < displayCount; ++i) {
-            final DisplayContent display = mRoot.mChildren.get(i);
-            displayReady(display.getDisplayId());
-        }
-
-
-        synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDefaultDisplayContentLocked();
+        synchronized (mWindowMap) {
             if (mMaxUiWidth > 0) {
-                displayContent.setMaxUiWidth(mMaxUiWidth);
+                mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
             }
-            readForcedDisplayPropertiesLocked(displayContent);
+            applyForcedPropertiesForDefaultDisplay();
+            mAnimator.ready();
             mDisplayReady = true;
-        }
-
-        try {
-            mActivityTaskManager.updateConfiguration(null);
-        } catch (RemoteException e) {
-        }
-
-        synchronized(mWindowMap) {
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
-            getDefaultDisplayContentLocked().configureDisplayPolicy();
         }
 
         try {
@@ -4450,17 +4433,6 @@
         updateCircularDisplayMaskIfNeeded();
     }
 
-    private void displayReady(int displayId) {
-        synchronized(mWindowMap) {
-            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            if (displayContent != null) {
-                mAnimator.addDisplayLocked(displayId);
-                displayContent.initializeDisplayBaseInfo();
-                reconfigureDisplayLocked(displayContent);
-            }
-        }
-    }
-
     public void systemReady() {
         mPolicy.systemReady();
         mTaskSnapshotController.systemReady();
@@ -5017,20 +4989,9 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
-                // Set some sort of reasonable bounds on the size of the display that we
-                // will try to emulate.
-                final int MIN_WIDTH = 200;
-                final int MIN_HEIGHT = 200;
-                final int MAX_SCALE = 2;
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    width = Math.min(Math.max(width, MIN_WIDTH),
-                            displayContent.mInitialDisplayWidth * MAX_SCALE);
-                    height = Math.min(Math.max(height, MIN_HEIGHT),
-                            displayContent.mInitialDisplayHeight * MAX_SCALE);
-                    setForcedDisplaySizeLocked(displayContent, width, height);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height);
+                    displayContent.setForcedSize(width, height);
                 }
             }
         } finally {
@@ -5052,12 +5013,7 @@
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    if (mode < 0 || mode > 1) {
-                        mode = 0;
-                    }
-                    setForcedDisplayScalingModeLocked(displayContent, mode);
-                    Settings.Global.putInt(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SCALING_FORCE, mode);
+                    displayContent.setForcedScalingMode(mode);
                 }
             }
         } finally {
@@ -5065,13 +5021,10 @@
         }
     }
 
-    private void setForcedDisplayScalingModeLocked(DisplayContent displayContent, int mode) {
-        Slog.i(TAG_WM, "Using display scaling mode: " + (mode == 0 ? "auto" : "off"));
-        displayContent.mDisplayScalingDisabled = (mode != 0);
-        reconfigureDisplayLocked(displayContent);
-    }
-
-    private void readForcedDisplayPropertiesLocked(final DisplayContent displayContent) {
+    /** The global settings only apply to default display. */
+    private void applyForcedPropertiesForDefaultDisplay() {
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
+        boolean changed = false;
         // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SIZE_FORCED);
@@ -5090,6 +5043,7 @@
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
                         displayContent.updateBaseDisplayMetrics(width, height,
                                 displayContent.mBaseDisplayDensity);
+                        changed = true;
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -5100,6 +5054,7 @@
         final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
         if (density != 0) {
             displayContent.mBaseDisplayDensity = density;
+            changed = true;
         }
 
         // Display scaling mode.
@@ -5108,14 +5063,12 @@
         if (mode != 0) {
             Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
             displayContent.mDisplayScalingDisabled = true;
+            changed = true;
         }
-    }
 
-    // displayContent must not be null
-    private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
-        Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
-        displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity);
-        reconfigureDisplayLocked(displayContent);
+        if (changed) {
+            reconfigureDisplayLocked(displayContent);
+        }
     }
 
     @Override
@@ -5132,10 +5085,8 @@
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
+                    displayContent.setForcedSize(displayContent.mInitialDisplayWidth,
                             displayContent.mInitialDisplayHeight);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SIZE_FORCED, "");
                 }
             }
         } finally {
@@ -5181,12 +5132,9 @@
         try {
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                if (displayContent != null && mCurrentUserId == targetUserId) {
-                    setForcedDisplayDensityLocked(displayContent, density);
+                if (displayContent != null) {
+                    displayContent.setForcedDensity(density, targetUserId);
                 }
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.DISPLAY_DENSITY_FORCED,
-                        Integer.toString(density), targetUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -5209,12 +5157,10 @@
         try {
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                if (displayContent != null && mCurrentUserId == callingUserId) {
-                    setForcedDisplayDensityLocked(displayContent,
-                            displayContent.mInitialDisplayDensity);
+                if (displayContent != null) {
+                    displayContent.setForcedDensity(displayContent.mInitialDisplayDensity,
+                            callingUserId);
                 }
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.DISPLAY_DENSITY_FORCED, "", callingUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -5241,18 +5187,6 @@
         return 0;
     }
 
-    /**
-     * Forces the given display to the use the specified density.
-     *
-     * @param displayContent the display to modify
-     * @param density the density in DPI to use
-     */
-    private void setForcedDisplayDensityLocked(@NonNull DisplayContent displayContent,
-            int density) {
-        displayContent.mBaseDisplayDensity = density;
-        reconfigureDisplayLocked(displayContent);
-    }
-
     void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) {
         if (!displayContent.isReady()) {
             return;
@@ -5306,9 +5240,7 @@
         displayInfo.overscanRight = right;
         displayInfo.overscanBottom = bottom;
 
-        mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
-                right, bottom);
-        mDisplaySettings.writeSettingsLocked();
+        mDisplaySettings.setOverscanLocked(displayInfo, left, top, right, bottom);
 
         reconfigureDisplayLocked(displayContent);
     }
@@ -6507,23 +6439,6 @@
         return mRoot.getDisplayContent(DEFAULT_DISPLAY);
     }
 
-    public void onDisplayAdded(int displayId) {
-        synchronized (mWindowMap) {
-            final Display display = mDisplayManager.getDisplay(displayId);
-            if (display != null) {
-                displayReady(displayId);
-            }
-            mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
-    public void onDisplayRemoved(int displayId) {
-        synchronized (mWindowMap) {
-            mAnimator.removeDisplayLocked(displayId);
-            mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
     public void onOverlayChanged() {
         synchronized (mWindowMap) {
             mRoot.forAllDisplays(displayContent -> {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf2d0df..bf77ba8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -233,9 +233,11 @@
     private int runDisplayScaling(PrintWriter pw) throws RemoteException {
         String scalingStr = getNextArgRequired();
         if ("auto".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr),
+                    DisplayContent.FORCE_SCALING_MODE_AUTO);
         } else if ("off".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr),
+                    DisplayContent.FORCE_SCALING_MODE_DISABLED);
         } else {
             getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
             return -1;