diff options
6 files changed, 217 insertions, 46 deletions
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java index 13ee47eb91c8..40b2f5ab852f 100644 --- a/services/core/java/com/android/server/display/BrightnessRangeController.java +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -43,14 +43,16 @@ class BrightnessRangeController { BrightnessRangeController(HighBrightnessModeController hbmController, Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { this(hbmController, modeChangeCallback, displayDeviceConfig, - new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags); + new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags, + displayToken, info); } BrightnessRangeController(HighBrightnessModeController hbmController, Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, - HdrClamper hdrClamper, DisplayManagerFlags flags) { + HdrClamper hdrClamper, DisplayManagerFlags flags, IBinder displayToken, + DisplayDeviceInfo info) { mHbmController = hbmController; mModeChangeCallback = modeChangeCallback; mHdrClamper = hdrClamper; @@ -60,10 +62,7 @@ class BrightnessRangeController { mNormalBrightnessModeController.resetNbmData( displayDeviceConfig.getLuxThrottlingData()); } - if (mUseHdrClamper) { - mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData()); - } - + updateHdrClamper(info, displayToken, displayDeviceConfig); } void dump(PrintWriter pw) { @@ -101,13 +100,12 @@ class BrightnessRangeController { displayDeviceConfig::getHdrBrightnessFromSdr); } ); - if (mUseHdrClamper) { - mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData()); - } + updateHdrClamper(info, token, displayDeviceConfig); } void stop() { mHbmController.stop(); + mHdrClamper.stop(); } void setAutoBrightnessEnabled(int state) { @@ -151,6 +149,18 @@ class BrightnessRangeController { return mHbmController.getTransitionPoint(); } + private void updateHdrClamper(DisplayDeviceInfo info, IBinder token, + DisplayDeviceConfig displayDeviceConfig) { + if (mUseHdrClamper) { + DisplayDeviceConfig.HighBrightnessModeData hbmData = + displayDeviceConfig.getHighBrightnessModeData(); + float minimumHdrPercentOfScreen = + hbmData == null ? -1f : hbmData.minimumHdrPercentOfScreen; + mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData(), info.width, + info.height, minimumHdrPercentOfScreen, token); + } + } + private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) { if (mUseNbmController) { boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean(); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index e791b0177d3e..ce98559abe30 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -687,7 +687,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback); mBrightnessRangeController = new BrightnessRangeController(hbmController, - modeChangeCallback, mDisplayDeviceConfig, mHandler, flags); + modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, + mDisplayDevice.getDisplayTokenLocked(), + mDisplayDevice.getDisplayDeviceInfoLocked()); mBrightnessThrottler = createBrightnessThrottlerLocked(); diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index bbc9ebf1d0ad..1652871963b9 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -552,7 +552,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessThrottler = createBrightnessThrottlerLocked(); mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController, - modeChangeCallback, mDisplayDeviceConfig, mHandler, flags); + modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, + mDisplayDevice.getDisplayTokenLocked(), + mDisplayDevice.getDisplayDeviceInfoLocked()); mDisplayBrightnessController = new DisplayBrightnessController(context, null, @@ -1896,15 +1898,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private HighBrightnessModeController createHbmControllerLocked( HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); - final IBinder displayToken = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked(); - final String displayUniqueId = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig(); + final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked(); + final String displayUniqueId = mDisplayDevice.getUniqueId(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked(); return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> @@ -3050,9 +3049,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal BrightnessRangeController getBrightnessRangeController( HighBrightnessModeController hbmController, Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { return new BrightnessRangeController(hbmController, - modeChangeCallback, displayDeviceConfig, handler, flags); + modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info); } DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java index a514136e62a2..e46edd9ab07a 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java @@ -17,9 +17,13 @@ package com.android.server.display.brightness.clamper; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; +import android.view.SurfaceControlHdrLayerInfoListener; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.config.HdrBrightnessData; import java.io.PrintWriter; @@ -33,11 +37,18 @@ public class HdrClamper { private final Runnable mDebouncer; + private final HdrLayerInfoListener mHdrListener; + @Nullable private HdrBrightnessData mHdrBrightnessData = null; + @Nullable + private IBinder mRegisteredDisplayToken = null; + private float mAmbientLux = Float.MAX_VALUE; + private boolean mHdrVisible = false; + private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX; private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX; @@ -47,6 +58,12 @@ public class HdrClamper { public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener, Handler handler) { + this(clamperChangeListener, handler, new Injector()); + } + + @VisibleForTesting + public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener, + Handler handler, Injector injector) { mClamperChangeListener = clamperChangeListener; mHandler = handler; mDebouncer = () -> { @@ -54,6 +71,10 @@ public class HdrClamper { mMaxBrightness = mDesiredMaxBrightness; mClamperChangeListener.onChanged(); }; + mHdrListener = injector.getHdrListener((visible) -> { + mHdrVisible = visible; + recalculateBrightnessCap(mHdrBrightnessData, mAmbientLux, mHdrVisible); + }, handler); } // Called in same looper: mHandler.getLooper() @@ -72,16 +93,37 @@ public class HdrClamper { */ public void onAmbientLuxChange(float ambientLux) { mAmbientLux = ambientLux; - recalculateBrightnessCap(mHdrBrightnessData, ambientLux); + recalculateBrightnessCap(mHdrBrightnessData, ambientLux, mHdrVisible); } /** * Updates brightness cap config. * Called in same looper: mHandler.getLooper() */ - public void resetHdrConfig(HdrBrightnessData data) { + @SuppressLint("AndroidFrameworkRequiresPermission") + public void resetHdrConfig(HdrBrightnessData data, int width, int height, + float minimumHdrPercentOfScreen, IBinder displayToken) { mHdrBrightnessData = data; - recalculateBrightnessCap(data, mAmbientLux); + mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen; + if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe + if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe + mHdrListener.unregister(mRegisteredDisplayToken); + mHdrVisible = false; + } + if (displayToken != null) { // new token not null, subscribe + mHdrListener.register(displayToken); + } + mRegisteredDisplayToken = displayToken; + } + recalculateBrightnessCap(data, mAmbientLux, mHdrVisible); + } + + /** Clean up all resources */ + @SuppressLint("AndroidFrameworkRequiresPermission") + public void stop() { + if (mRegisteredDisplayToken != null) { + mHdrListener.unregister(mRegisteredDisplayToken); + } } /** @@ -98,13 +140,28 @@ public class HdrClamper { pw.println(" mAmbientLux=" + mAmbientLux); } - private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux) { - if (data == null) { - mHandler.removeCallbacks(mDebouncer); + private void reset() { + if (mMaxBrightness == PowerManager.BRIGHTNESS_MAX + && mDesiredMaxBrightness == PowerManager.BRIGHTNESS_MAX && mTransitionRate == -1f + && mDesiredTransitionRate == -1f) { // already done reset, do nothing return; } - float expectedMaxBrightness = findBrightnessLimit(data, ambientLux); + mHandler.removeCallbacks(mDebouncer); + mMaxBrightness = PowerManager.BRIGHTNESS_MAX; + mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX; + mDesiredTransitionRate = -1f; + mTransitionRate = 1f; + mClamperChangeListener.onChanged(); + } + private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux, + boolean hdrVisible) { + if (data == null || !hdrVisible) { + reset(); + return; + } + + float expectedMaxBrightness = findBrightnessLimit(data, ambientLux); if (mMaxBrightness == expectedMaxBrightness) { mDesiredMaxBrightness = mMaxBrightness; mDesiredTransitionRate = -1f; @@ -127,6 +184,8 @@ public class HdrClamper { mHandler.removeCallbacks(mDebouncer); mHandler.postDelayed(mDebouncer, debounceTime); } + // do nothing if expectedMaxBrightness == mDesiredMaxBrightness + // && expectedMaxBrightness != mMaxBrightness } private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) { @@ -143,4 +202,36 @@ public class HdrClamper { } return foundMaxBrightness; } + + @FunctionalInterface + interface HdrListener { + void onHdrVisible(boolean visible); + } + + static class HdrLayerInfoListener extends SurfaceControlHdrLayerInfoListener { + private final HdrListener mHdrListener; + + private final Handler mHandler; + + private float mHdrMinPixels = Float.MAX_VALUE; + + HdrLayerInfoListener(HdrListener hdrListener, Handler handler) { + mHdrListener = hdrListener; + mHandler = handler; + } + + @Override + public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, int maxW, + int maxH, int flags, float maxDesiredHdrSdrRatio) { + mHandler.post(() -> + mHdrListener.onHdrVisible( + numberOfHdrLayers > 0 && (float) (maxW * maxH) >= mHdrMinPixels)); + } + } + + static class Injector { + HdrLayerInfoListener getHdrListener(HdrListener hdrListener, Handler handler) { + return new HdrLayerInfoListener(hdrListener, handler); + } + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java index f57702df5b21..dca69eb6dd98 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java @@ -1851,9 +1851,9 @@ public final class DisplayPowerController2Test { BrightnessRangeController getBrightnessRangeController( HighBrightnessModeController hbmController, Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { return new BrightnessRangeController(hbmController, modeChangeCallback, - displayDeviceConfig, mHdrClamper, mFlags); + displayDeviceConfig, mHdrClamper, mFlags, displayToken, info); } @Override diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java index 37d966d044c5..c3322eca6104 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java @@ -20,6 +20,12 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.IBinder; import android.os.PowerManager; import androidx.test.filters.SmallTest; @@ -31,6 +37,7 @@ import com.android.server.testutils.TestHandler; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -40,7 +47,20 @@ import java.util.Map; @SmallTest public class HdrClamperTest { - public static final float FLOAT_TOLERANCE = 0.0001f; + private static final float FLOAT_TOLERANCE = 0.0001f; + private static final long SEND_TIME_TOLERANCE = 100; + + private static final HdrBrightnessData TEST_HDR_DATA = new HdrBrightnessData( + Map.of(500f, 0.6f), + /* brightnessIncreaseDebounceMillis= */ 1000, + /* brightnessIncreaseDurationMillis= */ 2000, + /* brightnessDecreaseDebounceMillis= */ 3000, + /* brightnessDecreaseDurationMillis= */4000 + ); + + private static final int WIDTH = 600; + private static final int HEIGHT = 800; + private static final float MIN_HDR_PERCENT = 0.5f; @Rule public MockitoRule mRule = MockitoJUnit.rule(); @@ -48,17 +68,31 @@ public class HdrClamperTest { @Mock private BrightnessClamperController.ClamperChangeListener mMockListener; + @Mock + private IBinder mMockBinder; + + @Mock + private HdrClamper.Injector mMockInjector; + + @Mock + private HdrClamper.HdrLayerInfoListener mMockHdrInfoListener; + OffsettableClock mClock = new OffsettableClock.Stopped(); private final TestHandler mTestHandler = new TestHandler(null, mClock); private HdrClamper mHdrClamper; - + private HdrClamper.HdrListener mHdrChangeListener; @Before public void setUp() { - mHdrClamper = new HdrClamper(mMockListener, mTestHandler); + when(mMockInjector.getHdrListener(any(), any())).thenReturn(mMockHdrInfoListener); + mHdrClamper = new HdrClamper(mMockListener, mTestHandler, mMockInjector); + ArgumentCaptor<HdrClamper.HdrListener> listenerCaptor = ArgumentCaptor.forClass( + HdrClamper.HdrListener.class); + verify(mMockInjector).getHdrListener(listenerCaptor.capture(), eq(mTestHandler)); + mHdrChangeListener = listenerCaptor.getValue(); configureClamper(); } @@ -68,20 +102,23 @@ public class HdrClamperTest { assertFalse(mTestHandler.hasMessagesOrCallbacks()); assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); } @Test - public void testClamper_AmbientLuxChangesBelowLimit() { + public void testClamper_AmbientLuxChangesBelowLimit_MaxDecrease() { mHdrClamper.onAmbientLuxChange(499); assertTrue(mTestHandler.hasMessagesOrCallbacks()); TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek(); - assertEquals(2000, msgInfo.sendTime); + assertSendTime(3000, msgInfo.sendTime); assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); - mClock.fastForward(2000); + mClock.fastForward(3000); mTestHandler.timeAdvance(); assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 4 } @Test @@ -91,33 +128,65 @@ public class HdrClamperTest { assertFalse(mTestHandler.hasMessagesOrCallbacks()); assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); } @Test public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() { mHdrClamper.onAmbientLuxChange(499); - mClock.fastForward(2000); + mClock.fastForward(3000); mTestHandler.timeAdvance(); mHdrClamper.onAmbientLuxChange(500); assertTrue(mTestHandler.hasMessagesOrCallbacks()); TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek(); - assertEquals(3000, msgInfo.sendTime); // 2000 + 1000 + assertSendTime(4000, msgInfo.sendTime); // 3000 + 1000 mClock.fastForward(1000); mTestHandler.timeAdvance(); assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(0.2f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 2 + } + + @Test + public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit() { + mHdrChangeListener.onHdrVisible(false); + mHdrClamper.onAmbientLuxChange(499); + + assertFalse(mTestHandler.hasMessagesOrCallbacks()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); + } + + @Test + public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit_ThenHdrOn() { + mHdrChangeListener.onHdrVisible(false); + mHdrClamper.onAmbientLuxChange(499); + mHdrChangeListener.onHdrVisible(true); + + assertTrue(mTestHandler.hasMessagesOrCallbacks()); + TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek(); + assertSendTime(3000, msgInfo.sendTime); + assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + + mClock.fastForward(3000); + mTestHandler.timeAdvance(); + assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE); + assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); + } + + // MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis() + // (in Handler.sendMessageDelayed) and then by subtracting SystemClock.uptimeMillis() + // (in TestHandler.sendMessageAtTime, there might be several milliseconds difference between + // SystemClock.uptimeMillis() calls, and subtracted value might be greater than added. + private static void assertSendTime(long expectedTime, long sendTime) { + assertTrue(expectedTime >= sendTime); + assertTrue(expectedTime - SEND_TIME_TOLERANCE < sendTime); } private void configureClamper() { - HdrBrightnessData data = new HdrBrightnessData( - Map.of(500f, 0.6f), - /* brightnessIncreaseDebounceMillis= */ 1000, - /* brightnessIncreaseDurationMillis= */ 1500, - /* brightnessDecreaseDebounceMillis= */ 2000, - /* brightnessDecreaseDurationMillis= */2500 - ); - mHdrClamper.resetHdrConfig(data); + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder); + mHdrChangeListener.onHdrVisible(true); } } |