Merge "HdrBrightnessModifier: adding LowPowerMode settings listener" into main
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index c091062..109b0a8 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -60,7 +60,8 @@
@IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
BRIGHTNESS_MAX_REASON_NONE,
BRIGHTNESS_MAX_REASON_THERMAL,
- BRIGHTNESS_MAX_REASON_POWER_IC
+ BRIGHTNESS_MAX_REASON_POWER_IC,
+ BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE
})
@Retention(RetentionPolicy.SOURCE)
public @interface BrightnessMaxReason {}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 12c3197..59fffe7 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -347,7 +347,7 @@
data.mDisplayDeviceConfig));
}
if (flags.useNewHdrBrightnessModifier()) {
- modifiers.add(new HdrBrightnessModifier(handler, listener, data));
+ modifiers.add(new HdrBrightnessModifier(handler, context, listener, data));
}
return modifiers;
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
index ae1801c..4ab4336 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
@@ -21,10 +21,15 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.ContentObserver;
import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,6 +49,11 @@
static final float DEFAULT_MAX_HDR_SDR_RATIO = 1.0f;
private static final float DEFAULT_HDR_LAYER_SIZE = -1.0f;
+ private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE);
+
+ private final ContentObserver mContentObserver;
+
private final SurfaceControlHdrLayerInfoListener mHdrListener =
new SurfaceControlHdrLayerInfoListener() {
@Override
@@ -52,7 +62,8 @@
boolean hdrLayerPresent = numberOfHdrLayers > 0;
mHandler.post(() -> HdrBrightnessModifier.this.onHdrInfoChanged(
hdrLayerPresent ? (float) (maxW * maxH) : DEFAULT_HDR_LAYER_SIZE,
- hdrLayerPresent ? maxDesiredHdrSdrRatio : DEFAULT_MAX_HDR_SDR_RATIO));
+ hdrLayerPresent ? Math.max(maxDesiredHdrSdrRatio,
+ DEFAULT_MAX_HDR_SDR_RATIO) : DEFAULT_MAX_HDR_SDR_RATIO));
}
};
@@ -62,6 +73,7 @@
private final Runnable mDebouncer;
private IBinder mRegisteredDisplayToken;
+ private boolean mContentObserverRegistered = false;
private DisplayDeviceConfig mDisplayDeviceConfig;
@Nullable
@@ -73,6 +85,8 @@
private float mAmbientLux = INVALID_LUX;
+ private boolean mLowPowerMode = false;
+
private Mode mMode = Mode.NO_HDR;
// The maximum brightness allowed for current lux
private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -81,17 +95,17 @@
private float mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
private float mPendingTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
- HdrBrightnessModifier(Handler handler,
+ HdrBrightnessModifier(Handler handler, Context context,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData displayData) {
- this(new Handler(handler.getLooper()), clamperChangeListener, new Injector(), displayData);
+ this(new Handler(handler.getLooper()), clamperChangeListener,
+ new Injector(context), displayData);
}
@VisibleForTesting
HdrBrightnessModifier(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
- Injector injector,
- BrightnessClamperController.DisplayDeviceData displayData) {
+ Injector injector, BrightnessClamperController.DisplayDeviceData displayData) {
mHandler = handler;
mClamperChangeListener = clamperChangeListener;
mInjector = injector;
@@ -100,6 +114,12 @@
mMaxBrightness = mPendingMaxBrightness;
mClamperChangeListener.onChanged();
};
+ mContentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onLowPowerModeChange();
+ }
+ };
mHandler.post(() -> onDisplayChanged(displayData));
}
@@ -135,12 +155,14 @@
pw.println(" mMaxDesiredHdrRatio=" + mMaxDesiredHdrRatio);
pw.println(" mHdrLayerSize=" + mHdrLayerSize);
pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mMode=" + mMode);
pw.println(" mMaxBrightness=" + mMaxBrightness);
pw.println(" mPendingMaxBrightness=" + mPendingMaxBrightness);
pw.println(" mTransitionRate=" + mTransitionRate);
pw.println(" mPendingTransitionRate=" + mPendingTransitionRate);
pw.println(" mHdrListener registered=" + (mRegisteredDisplayToken != null));
+ pw.println(" mContentObserverRegistered=" + mContentObserverRegistered);
}
// Called in DisplayControllerHandler
@@ -182,7 +204,25 @@
} else {
registerHdrListener(displayData.mDisplayToken);
}
- recalculate(data, mMaxDesiredHdrRatio);
+ if (data == null || data.allowInLowPowerMode) {
+ unregisterContentObserver();
+ } else {
+ registerContentObserver();
+ }
+
+ Mode newMode = recalculateMode(data);
+ // mode changed, or mode was HDR and HdrBrightnessData changed
+ boolean needToNotifyChange = mMode != newMode
+ || (mMode != HdrBrightnessModifier.Mode.NO_HDR && data != mHdrBrightnessData);
+ mMode = newMode;
+ mHdrBrightnessData = data;
+ mMaxBrightness = findBrightnessLimit(mHdrBrightnessData, mAmbientLux);
+
+ if (needToNotifyChange) {
+ // data changed, reset custom transition rate
+ mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+ mClamperChangeListener.onChanged();
+ }
}
// Called in DisplayControllerHandler, when any modifier state changes
@@ -226,30 +266,6 @@
}
// Called in DisplayControllerHandler
- private void recalculate(@Nullable HdrBrightnessData data, float maxDesiredHdrRatio) {
- Mode newMode = recalculateMode(data);
- // if HDR mode changed, notify changed
- boolean needToNotifyChange = mMode != newMode;
- // If HDR mode is active, we need to check if other HDR params are changed
- if (mMode != HdrBrightnessModifier.Mode.NO_HDR) {
- if (!BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrRatio)
- || data != mHdrBrightnessData) {
- needToNotifyChange = true;
- }
- }
-
- mMode = newMode;
- mHdrBrightnessData = data;
- mMaxDesiredHdrRatio = maxDesiredHdrRatio;
-
- if (needToNotifyChange) {
- // data or hdr layer changed, reset custom transition rate
- mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
- mClamperChangeListener.onChanged();
- }
- }
-
- // Called in DisplayControllerHandler
private Mode recalculateMode(@Nullable HdrBrightnessData data) {
// no config
if (data == null) {
@@ -259,6 +275,10 @@
if (mHdrLayerSize == DEFAULT_HDR_LAYER_SIZE) {
return Mode.NO_HDR;
}
+ // low power mode and not allowed in low power mode
+ if (!data.allowInLowPowerMode && mLowPowerMode) {
+ return Mode.NO_HDR;
+ }
// HDR layer < minHdr % for Nbm
if (mHdrLayerSize < mScreenSize * data.minimumHdrPercentOfScreenForNbm) {
return Mode.NO_HDR;
@@ -271,6 +291,16 @@
return Mode.HBM_HDR;
}
+ private void onLowPowerModeChange() {
+ mLowPowerMode = mInjector.isLowPowerMode();
+ Mode newMode = recalculateMode(mHdrBrightnessData);
+ if (newMode != mMode) {
+ mMode = newMode;
+ mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+ mClamperChangeListener.onChanged();
+ }
+ }
+
private float getMaxBrightness(Mode mode, float maxBrightness, HdrBrightnessData data) {
if (mode == Mode.NBM_HDR) {
return Math.min(data.hbmTransitionPoint, maxBrightness);
@@ -282,7 +312,13 @@
}
// Called in DisplayControllerHandler
- private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
+ private float findBrightnessLimit(@Nullable HdrBrightnessData data, float ambientLux) {
+ if (data == null) {
+ return PowerManager.BRIGHTNESS_MAX;
+ }
+ if (ambientLux == INVALID_LUX) {
+ return PowerManager.BRIGHTNESS_MAX;
+ }
float foundAmbientBoundary = Float.MAX_VALUE;
float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
for (Map.Entry<Float, Float> brightnessPoint :
@@ -300,7 +336,17 @@
// Called in DisplayControllerHandler
private void onHdrInfoChanged(float hdrLayerSize, float maxDesiredHdrSdrRatio) {
mHdrLayerSize = hdrLayerSize;
- recalculate(mHdrBrightnessData, maxDesiredHdrSdrRatio);
+ Mode newMode = recalculateMode(mHdrBrightnessData);
+ // mode changed, or mode was HDR and maxDesiredHdrRatio changed
+ boolean needToNotifyChange = mMode != newMode
+ || (mMode != HdrBrightnessModifier.Mode.NO_HDR
+ && !BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrSdrRatio));
+ mMode = newMode;
+ mMaxDesiredHdrRatio = maxDesiredHdrSdrRatio;
+ if (needToNotifyChange) {
+ mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+ mClamperChangeListener.onChanged();
+ }
}
// Called in DisplayControllerHandler
@@ -324,12 +370,36 @@
}
}
+ // Called in DisplayControllerHandler
+ private void registerContentObserver() {
+ if (!mContentObserverRegistered) {
+ mInjector.registerContentObserver(mContentObserver, mLowPowerModeSetting);
+ mContentObserverRegistered = true;
+ mLowPowerMode = mInjector.isLowPowerMode();
+ }
+ }
+
+ // Called in DisplayControllerHandler
+ private void unregisterContentObserver() {
+ if (mContentObserverRegistered) {
+ mInjector.unregisterContentObserver(mContentObserver);
+ mContentObserverRegistered = false;
+ mLowPowerMode = false;
+ }
+ }
+
private enum Mode {
NO_HDR, NBM_HDR, HBM_HDR
}
@SuppressLint("MissingPermission")
static class Injector {
+ private final Context mContext;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
void registerHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
listener.register(token);
}
@@ -337,5 +407,19 @@
void unregisterHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
listener.unregister(token);
}
+
+ void registerContentObserver(ContentObserver observer, Uri uri) {
+ mContext.getContentResolver().registerContentObserver(uri, false,
+ observer, UserHandle.USER_ALL);
+ }
+
+ void unregisterContentObserver(ContentObserver observer) {
+ mContext.getContentResolver().unregisterContentObserver(observer);
+ }
+
+ boolean isLowPowerMode() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0;
+ }
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
index 0ed96ae..bb025cc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
@@ -16,7 +16,10 @@
package com.android.server.display.brightness.clamper
+import android.content.Context
+import android.database.ContentObserver
import android.hardware.display.DisplayManagerInternal
+import android.net.Uri
import android.os.IBinder
import android.os.PowerManager.BRIGHTNESS_MAX
import android.util.Spline
@@ -51,7 +54,7 @@
private val stoppedClock = OffsettableClock.Stopped()
private val testHandler = TestHandler(null, stoppedClock)
- private val testInjector = TestInjector()
+ private val testInjector = TestInjector(mock<Context>())
private val mockChangeListener = mock<ClamperChangeListener>()
private val mockDisplayDeviceConfig = mock<DisplayDeviceConfig>()
private val mockDisplayBinder = mock<IBinder>()
@@ -63,14 +66,14 @@
private val dummyData = createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinder)
@Test
- fun `change listener is not called on init`() {
+ fun changeListenerIsNotCalledOnInit() {
initHdrModifier()
verify(mockChangeListener, never()).onChanged()
}
@Test
- fun `hdr listener registered on init if hdr data is present`() {
+ fun hdrListenerRegisteredOnInit_hdrDataPresent() {
initHdrModifier()
assertThat(testInjector.registeredHdrListener).isNotNull()
@@ -78,22 +81,19 @@
}
@Test
- fun `hdr listener not registered on init if hdr data is missing`() {
- initHdrModifier(null)
-
- testHandler.flush()
+ fun hdrListenerNotRegisteredOnInit_hdrDataMissing() {
+ initHdrModifier(hdrBrightnessData = null)
assertThat(testInjector.registeredHdrListener).isNull()
assertThat(testInjector.registeredToken).isNull()
}
@Test
- fun `unsubscribes hdr listener when display changed with no hdr data`() {
+ fun unsubscribeHdrListener_displayChangedWithNoHdrData() {
initHdrModifier()
whenever(mockDisplayDeviceConfig.hdrBrightnessData).thenReturn(null)
modifier.onDisplayChanged(dummyData)
- testHandler.flush()
assertThat(testInjector.registeredHdrListener).isNull()
assertThat(testInjector.registeredToken).isNull()
@@ -101,12 +101,11 @@
}
@Test
- fun `resubscribes hdr listener when display changed with different token`() {
+ fun resubscribesHdrListener_displayChangedWithDifferentToken() {
initHdrModifier()
modifier.onDisplayChanged(
createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinderOther))
- testHandler.flush()
assertThat(testInjector.registeredHdrListener).isNotNull()
assertThat(testInjector.registeredToken).isEqualTo(mockDisplayBinderOther)
@@ -114,7 +113,28 @@
}
@Test
- fun `test NO_HDR mode`() {
+ fun contentObserverNotRegisteredOnInit_hdrDataMissing() {
+ initHdrModifier(null)
+
+ assertThat(testInjector.registeredContentObserver).isNull()
+ }
+
+ @Test
+ fun contentObserverNotRegisteredOnInit_allowedInLowPowerMode() {
+ initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = true))
+
+ assertThat(testInjector.registeredContentObserver).isNull()
+ }
+
+ @Test
+ fun contentObserverRegisteredOnInit_notAllowedInLowPowerMode() {
+ initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = false))
+
+ assertThat(testInjector.registeredContentObserver).isNotNull()
+ }
+
+ @Test
+ fun testNoHdrMode() {
initHdrModifier()
// screen size = 10_000
setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -131,7 +151,7 @@
}
@Test
- fun `test NBM_HDR mode`() {
+ fun testNbmHdrMode() {
initHdrModifier()
// screen size = 10_000
val transitionPoint = 0.55f
@@ -157,7 +177,7 @@
}
@Test
- fun `test HBM_HDR mode`() {
+ fun testHbmHdrMode() {
initHdrModifier()
// screen size = 10_000
setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -182,7 +202,7 @@
}
@Test
- fun `test display change no HDR content`() {
+ fun testDisplayChange_noHdrContent() {
initHdrModifier()
setupDisplay(width = 100, height = 100)
assertModifierState()
@@ -195,7 +215,7 @@
}
@Test
- fun `test display change with HDR content`() {
+ fun testDisplayChange_hdrContent() {
initHdrModifier()
setupDisplay(width = 100, height = 100)
setupHdrLayer(width = 100, height = 100, maxHdrRatio = 5f)
@@ -218,7 +238,7 @@
}
@Test
- fun `test ambient lux decrease above maxBrightnessLimits no HDR`() {
+ fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitNoHdr() {
initHdrModifier()
modifier.setAmbientLux(1000f)
setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -234,7 +254,7 @@
}
@Test
- fun `test ambient lux decrease above maxBrightnessLimits with HDR`() {
+ fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitWithHdr() {
initHdrModifier()
modifier.setAmbientLux(1000f)
setupDisplay(width = 200, height = 200, hdrBrightnessData = createHdrBrightnessData(
@@ -260,7 +280,7 @@
}
@Test
- fun `test ambient lux decrease below maxBrightnessLimits no HDR`() {
+ fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitNoHdr() {
initHdrModifier()
modifier.setAmbientLux(1000f)
setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -276,7 +296,7 @@
}
@Test
- fun `test ambient lux decrease below maxBrightnessLimits with HDR`() {
+ fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitWithHdr() {
initHdrModifier()
modifier.setAmbientLux(1000f)
val maxBrightness = 0.6f
@@ -322,6 +342,23 @@
)
}
+ @Test
+ fun testLowPower_notAllowedInLowPower() {
+ initHdrModifier()
+ setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
+ allowInLowPowerMode = false
+ ))
+ setupHdrLayer(width = 100, height = 100)
+ clearInvocations(mockChangeListener)
+
+ testInjector.isLowPower = true
+ testInjector.registeredContentObserver!!.onChange(true)
+
+ verify(mockChangeListener).onChanged()
+ assertModifierState()
+ }
+
+ // Helper functions
private fun setupHdrLayer(width: Int = 100, height: Int = 100, maxHdrRatio: Float = 0.8f) {
testInjector.registeredHdrListener!!.onHdrInfoChanged(
mockDisplayBinder, 1, width, height, 0, maxHdrRatio
@@ -345,7 +382,6 @@
width = width,
height = height
))
- testHandler.flush()
}
private fun initHdrModifier(hdrBrightnessData: HdrBrightnessData? = createHdrBrightnessData()) {
@@ -384,9 +420,12 @@
assertThat(stateBuilder.customAnimationRate).isEqualTo(animationRate)
}
- internal class TestInjector : Injector() {
+ internal class TestInjector(context: Context) : Injector(context) {
var registeredHdrListener: SurfaceControlHdrLayerInfoListener? = null
var registeredToken: IBinder? = null
+ var registeredContentObserver: ContentObserver? = null
+
+ var isLowPower: Boolean = false
override fun registerHdrListener(
listener: SurfaceControlHdrLayerInfoListener, token: IBinder
@@ -401,5 +440,17 @@
registeredHdrListener = null
registeredToken = null
}
+
+ override fun registerContentObserver(observer: ContentObserver, uri: Uri) {
+ registeredContentObserver = observer
+ }
+
+ override fun unregisterContentObserver(observer: ContentObserver) {
+ registeredContentObserver = null
+ }
+
+ override fun isLowPowerMode(): Boolean {
+ return isLowPower
+ }
}
}
\ No newline at end of file