Fix that changes in desiredHdrSdrRatio doesn't update brightness

When onHdrInfoChanged is received from SF and the resulting
maxDesiredHdrSdrRatio changes ensure that the hbmChanged
callback is invoked even though the high-level mode did not
otherwise change. Otherwise the display brightness does not
react to the change, and so changes in the desired HDR headroom
does not result in changes to the actual display brightness.

Bug: 234181960
Test: HighBrightnessModeControllerTest#testHdrRespectsChangingDesiredHdrSdrRatio
Change-Id: I41f8062781493a73d4f1752c6c88d38dfad74909
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index c04c279..39172b8 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -25,6 +25,7 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.MathUtils;
@@ -33,6 +34,7 @@
 import android.view.SurfaceControlHdrLayerInfoListener;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 import com.android.server.display.DisplayManagerService.Clock;
@@ -99,6 +101,7 @@
     private boolean mIsHdrLayerPresent = false;
     // mMaxDesiredHdrSdrRatio should only be applied when there is a valid backlight->nits mapping
     private float mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+    private boolean mForceHbmChangeCallback = false;
     private boolean mIsBlockedByLowPowerMode = false;
     private int mWidth;
     private int mHeight;
@@ -484,7 +487,8 @@
     private void updateHbmMode() {
         int newHbmMode = calculateHighBrightnessMode();
         updateHbmStats(newHbmMode);
-        if (mHbmMode != newHbmMode) {
+        if (mHbmMode != newHbmMode || mForceHbmChangeCallback) {
+            mForceHbmChangeCallback = false;
             mHbmMode = newHbmMode;
             mHbmChangeCallback.run();
         }
@@ -600,26 +604,32 @@
         public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
                 int maxW, int maxH, int flags, float maxDesiredHdrSdrRatio) {
             mHandler.post(() -> {
+                Trace.traceBegin(Trace.TRACE_TAG_POWER, "HBMController#onHdrInfoChanged");
                 mIsHdrLayerPresent = numberOfHdrLayers > 0
                         && (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
                                    * mHbmData.minimumHdrPercentOfScreen);
 
-                final float candidateDesiredHdrSdrRatio =
+                float candidateDesiredHdrSdrRatio =
                         mIsHdrLayerPresent && mHdrBrightnessCfg != null
                                 ? maxDesiredHdrSdrRatio
                                 : DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
 
-                if (candidateDesiredHdrSdrRatio >= 1.0f) {
-                    mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
-                } else {
+                if (candidateDesiredHdrSdrRatio < 1.0f) {
                     Slog.w(TAG, "Ignoring invalid desired HDR/SDR Ratio: "
                             + candidateDesiredHdrSdrRatio);
-                    mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+                    candidateDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+                }
+
+                if (!BrightnessSynchronizer.floatEquals(
+                        mMaxDesiredHdrSdrRatio, candidateDesiredHdrSdrRatio)) {
+                    mForceHbmChangeCallback = true;
+                    mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
                 }
 
                 // Calling the brightness update so that we can recalculate
                 // brightness with HDR in mind.
                 onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
+                Trace.traceEnd(Trace.TRACE_TAG_POWER);
             });
         }
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 76e6ec7..8e01a11 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -32,8 +32,10 @@
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -390,6 +392,35 @@
         assertEquals(Float.POSITIVE_INFINITY, hbmc.getHdrBrightnessValue(), 0.0);
     }
 
+    @Test
+    public void testHdrRespectsChangingDesiredHdrSdrRatio() {
+        final Runnable hbmChangedCallback = mock(Runnable.class);
+        final HighBrightnessModeController hbmc = new TestHbmBuilder()
+                .setClock(new OffsettableClock())
+                .setHdrBrightnessConfig(mHdrBrightnessDeviceConfigMock)
+                .setHbmChangedCallback(hbmChangedCallback)
+                .build();
+
+        // Passthrough return the max desired hdr/sdr ratio
+        when(mHdrBrightnessDeviceConfigMock.getHdrBrightnessFromSdr(anyFloat(), anyFloat()))
+                .thenAnswer(i -> i.getArgument(1));
+
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/, 2.0f /*maxDesiredHdrSdrRatio*/);
+        advanceTime(0);
+        assertEquals(2.0f, hbmc.getHdrBrightnessValue(), EPSILON);
+        verify(hbmChangedCallback, times(1)).run();
+
+        // Verify that a change in only the desired hdrSdrRatio still results in the changed
+        // callback being invoked
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/,
+                3.0f /*maxDesiredHdrSdrRatio*/);
+        advanceTime(0);
+        assertEquals(3.0f, hbmc.getHdrBrightnessValue(), 0.0);
+        verify(hbmChangedCallback, times(2)).run();
+    }
+
 
     @Test
     public void testHdrTrumpsSunlight() {
@@ -698,6 +729,7 @@
     private class TestHbmBuilder {
         OffsettableClock mClock;
         HighBrightnessModeController.HdrBrightnessDeviceConfig mHdrBrightnessCfg;
+        Runnable mHdrChangedCallback = () -> {};
 
         TestHbmBuilder setClock(OffsettableClock clock) {
             mClock = clock;
@@ -711,6 +743,11 @@
             return this;
         }
 
+        TestHbmBuilder setHbmChangedCallback(Runnable runnable) {
+            mHdrChangedCallback = runnable;
+            return this;
+        }
+
         HighBrightnessModeController build() {
             initHandler(mClock);
             if (mHighBrightnessModeMetadata == null) {
@@ -718,8 +755,8 @@
             }
             return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
                     DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
-                    DEFAULT_HBM_DATA, mHdrBrightnessCfg, () -> {}, mHighBrightnessModeMetadata,
-                    mContextSpy);
+                    DEFAULT_HBM_DATA, mHdrBrightnessCfg, mHdrChangedCallback,
+                    mHighBrightnessModeMetadata, mContextSpy);
         }
 
     }