Merge "Implement Phase 4 of per-display power states"
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3ac2185..004e481 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -89,6 +89,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;
@@ -96,6 +97,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;
@@ -112,7 +114,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;
@@ -235,15 +236,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);
+                }
             }
         }
     };
@@ -257,13 +271,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.
@@ -317,11 +334,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<>();
 
@@ -372,7 +384,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();
@@ -589,7 +601,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;
         }
@@ -602,38 +614,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 {
@@ -987,6 +1001,8 @@
             recordTopInsetLocked(display);
         }
         addDisplayPowerControllerLocked(displayId);
+        mDisplayStates.append(displayId, Display.STATE_OFF);
+        mDisplayBrightnesses.append(displayId, mDisplayDefaultBrightness);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
 
@@ -1021,6 +1037,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);
@@ -1035,15 +1053,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).
@@ -1052,12 +1061,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;
     }
@@ -1587,13 +1600,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 09c9aab..f488260 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -685,8 +685,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 e35becc..979c3b8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -588,7 +588,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 a843af5..45c38b4 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;
@@ -149,6 +150,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];