Implement Phase 4 of per-display power states

Remove the concept of mGlobalDisplayState and instead track the state of
each display individually. The internal methods of DisplayManagerService
have been updated to allow for displays to update individually, however
for this phase there is temporary code which still makes every display
update when the default display state is updated.

Bug: 138328918
Test: manual
Change-Id: I3b3d8df1d0fc0cd7934474a3664a69bde585504f
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index dd31f04..4f6d16f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -88,6 +88,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -95,6 +96,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
@@ -111,7 +113,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Consumer;
@@ -234,15 +235,28 @@
     private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
         @Override
         public void requestDisplayState(int displayId, int state, float brightness) {
+            // TODO (b/168210494): Stop applying default display state to all displays.
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                return;
+            }
+            final int[] displayIds;
+            synchronized (mSyncRoot) {
+                displayIds = mLogicalDisplayMapper.getDisplayIdsLocked();
+            }
+
             // The order of operations is important for legacy reasons.
             if (state == Display.STATE_OFF) {
-                requestGlobalDisplayStateInternal(state, brightness);
+                for (int id : displayIds) {
+                    requestDisplayStateInternal(id, state, brightness);
+                }
             }
 
             mDisplayPowerCallbacks.onDisplayStateChange(state);
 
             if (state != Display.STATE_OFF) {
-                requestGlobalDisplayStateInternal(state, brightness);
+                for (int id : displayIds) {
+                    requestDisplayStateInternal(id, state, brightness);
+                }
             }
         }
     };
@@ -256,13 +270,16 @@
     /** The {@link Handler} used by all {@link DisplayPowerController}s. */
     private Handler mPowerHandler;
 
-    // The overall display state, independent of changes that might influence one
-    // display or another in particular.
-    private int mGlobalDisplayState = Display.STATE_ON;
+    // A map from LogicalDisplay ID to display power state.
+    @GuardedBy("mSyncRoot")
+    private final SparseIntArray mDisplayStates = new SparseIntArray();
 
-    // The overall display brightness.
-    // For now, this only applies to the default display but we may split it up eventually.
-    private float mGlobalDisplayBrightness;
+    // A map from LogicalDisplay ID to display brightness.
+    @GuardedBy("mSyncRoot")
+    private final SparseArray<Float> mDisplayBrightnesses = new SparseArray<>();
+
+    // The default brightness.
+    private final float mDisplayDefaultBrightness;
 
     // Set to true when there are pending display changes that have yet to be applied
     // to the surface flinger state.
@@ -316,11 +333,6 @@
     // DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY flag set.
     private final int mDefaultDisplayDefaultColorMode;
 
-    // Temporary list of deferred work to perform when setting the display state.
-    // Only used by requestDisplayState.  The field is self-synchronized and only
-    // intended for use inside of the requestGlobalDisplayStateInternal function.
-    private final ArrayList<Runnable> mTempDisplayStateWorkQueue = new ArrayList<Runnable>();
-
     // Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
     private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
 
@@ -371,7 +383,7 @@
         mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
 
         PowerManager pm = mContext.getSystemService(PowerManager.class);
-        mGlobalDisplayBrightness = pm.getBrightnessConstraint(
+        mDisplayDefaultBrightness = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
         mCurrentUserId = UserHandle.USER_SYSTEM;
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
@@ -588,7 +600,7 @@
         }
     }
 
-    private void requestGlobalDisplayStateInternal(int state, float brightnessState) {
+    private void requestDisplayStateInternal(int displayId, int state, float brightnessState) {
         if (state == Display.STATE_UNKNOWN) {
             state = Display.STATE_ON;
         }
@@ -601,38 +613,40 @@
             brightnessState = PowerManager.BRIGHTNESS_MAX;
         }
 
-        synchronized (mTempDisplayStateWorkQueue) {
-            try {
-                // Update the display state within the lock.
-                // Note that we do not need to schedule traversals here although it
-                // may happen as a side-effect of displays changing state.
-                synchronized (mSyncRoot) {
-                    if (mGlobalDisplayState == state
-                            && mGlobalDisplayBrightness == brightnessState) {
-                        return; // no change
-                    }
+        // Update the display state within the lock.
+        // Note that we do not need to schedule traversals here although it
+        // may happen as a side-effect of displays changing state.
+        final Runnable runnable;
+        final String traceMessage;
+        synchronized (mSyncRoot) {
+            final int index = mDisplayStates.indexOfKey(displayId);
 
-                    Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
-                            + Display.stateToString(state)
-                            + ", brightness=" + brightnessState + ")");
-
-                    mGlobalDisplayState = state;
-                    mGlobalDisplayBrightness = brightnessState;
-                    applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
-                }
-
-                // Setting the display power state can take hundreds of milliseconds
-                // to complete so we defer the most expensive part of the work until
-                // after we have exited the critical section to avoid blocking other
-                // threads for a long time.
-                for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
-                    mTempDisplayStateWorkQueue.get(i).run();
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_POWER);
-            } finally {
-                mTempDisplayStateWorkQueue.clear();
+            if (index < 0 || (mDisplayStates.valueAt(index) == state
+                    && BrightnessSynchronizer.floatEquals(mDisplayBrightnesses.valueAt(index),
+                    brightnessState))) {
+                return; // Display no longer exists or no change.
             }
+
+            traceMessage = "requestDisplayStateInternal("
+                    + displayId + ", "
+                    + Display.stateToString(state)
+                    + ", brightness=" + brightnessState + ")";
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, traceMessage, displayId);
+
+            mDisplayStates.setValueAt(index, state);
+            mDisplayBrightnesses.setValueAt(index, brightnessState);
+            runnable = updateDisplayStateLocked(
+                    mLogicalDisplayMapper.getLocked(displayId).getPrimaryDisplayDeviceLocked());
         }
+
+        // Setting the display power state can take hundreds of milliseconds
+        // to complete so we defer the most expensive part of the work until
+        // after we have exited the critical section to avoid blocking other
+        // threads for a long time.
+        if (runnable != null) {
+            runnable.run();
+        }
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, traceMessage, displayId);
     }
 
     private class SettingsObserver extends ContentObserver {
@@ -986,6 +1000,8 @@
             recordTopInsetLocked(display);
         }
         addDisplayPowerControllerLocked(displayId);
+        mDisplayStates.append(displayId, Display.STATE_OFF);
+        mDisplayBrightnesses.append(displayId, mDisplayDefaultBrightness);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
 
@@ -1020,6 +1036,8 @@
     private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
         mDisplayPowerControllers.delete(displayId);
+        mDisplayStates.delete(displayId);
+        mDisplayBrightnesses.delete(displayId);
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
         sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         scheduleTraversalLocked(false);
@@ -1034,15 +1052,6 @@
         handleLogicalDisplayChangedLocked(display);
     }
 
-    private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
-        mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
-            Runnable runnable = updateDisplayStateLocked(device);
-            if (runnable != null) {
-                workQueue.add(runnable);
-            }
-        });
-    }
-
     private Runnable updateDisplayStateLocked(DisplayDevice device) {
         // Blank or unblank the display immediately to match the state requested
         // by the display power controller (if known).
@@ -1051,12 +1060,16 @@
             // TODO - b/170498827 The rules regarding what display state to apply to each
             // display will depend on the configuration/mapping of logical displays.
             // Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
-            int state = mGlobalDisplayState;
             final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
-            if (display != null && !display.isEnabled()) {
+            final int state;
+            final int displayId = display.getDisplayIdLocked();
+            if (display.isEnabled()) {
+                state = mDisplayStates.get(displayId);
+            } else {
                 state = Display.STATE_OFF;
             }
-            return device.requestDisplayStateLocked(state, mGlobalDisplayBrightness);
+            final float brightness = mDisplayBrightnesses.get(displayId);
+            return device.requestDisplayStateLocked(state, brightness);
         }
         return null;
     }
@@ -1591,13 +1604,24 @@
             pw.println("  mOnlyCode=" + mOnlyCore);
             pw.println("  mSafeMode=" + mSafeMode);
             pw.println("  mPendingTraversal=" + mPendingTraversal);
-            pw.println("  mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
             pw.println("  mViewports=" + mViewports);
             pw.println("  mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
             pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
             pw.println("  mStableDisplaySize=" + mStableDisplaySize);
             pw.println("  mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
 
+            pw.println();
+            final int displayStateCount = mDisplayStates.size();
+            pw.println("Display States: size=" + displayStateCount);
+            for (int i = 0; i < displayStateCount; i++) {
+                final int displayId = mDisplayStates.keyAt(i);
+                final int displayState = mDisplayStates.valueAt(i);
+                final float brightness = mDisplayBrightnesses.valueAt(i);
+                pw.println("  Display Id=" + displayId);
+                pw.println("  Display State=" + Display.stateToString(displayState));
+                pw.println("  Display Brightness=" + brightness);
+            }
+
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 309271c..6566cc39 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -680,8 +680,6 @@
     }
 
     private void initialize() {
-        // Initialize the power state object for the default display.
-        // In the future, we might manage multiple displays independently.
         mPowerState = new DisplayPowerState(mBlanker,
                 mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
 
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index a17a294..30ad05c 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -585,7 +585,7 @@
     }
 
     /**
-     * Swap the underlying {@link DisplayDevice} with the specificed LogicalDisplay.
+     * Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
      *
      * @param targetDisplay The display with which to swap display-devices.
      * @return {@code true} if the displays were swapped, {@code false} otherwise.
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index fc3ba35..f18be3d 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import android.content.Context;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -143,6 +144,10 @@
         return null;
     }
 
+    public int[] getDisplayIdsLocked() {
+        return getDisplayIdsLocked(Process.SYSTEM_UID);
+    }
+
     public int[] getDisplayIdsLocked(int callingUid) {
         final int count = mLogicalDisplays.size();
         int[] displayIds = new int[count];