Reflect BrightnessThrottler effects in Settings, UI and ABC
Currently, display brightness limitations by BrightnessThrottler are applied as a brightness transform in DisplayPowerController similarly to pre-display timeout dimming and low power mode. This is intentional, and was done in an attempt to keep the addition of brightness throttling as unintrusive as possible. This design choice means that the following entities are not aware of brightness throttling: 1. Brightness slider in SysUI 2. AutomaticBrightnessController (ABC) 3. Settings Of particular concern is #1 and #2, as it means that the display brightness the user sees and the display brightness the SysUI slider can effect are out of sync. Combined with the fact that ABC doesn't know about brightness throttling, this means that ABC can learn the wrong user preferences while brightness is throttled. To fix this, we adjust the usage of BrightnessThrottler inside DisplayPowerController and ABC to mimic that of HighBrightnessModeController, which also dynamically adjusts maximum display brightness (for different reasons). Specifically, BrightnessThrottler results are now, 1. Captured in Settings, to match HighBrightnessModeController's behavior 2. Captured in BrightnessInfo, so that the maximum SysUI slider value is correct 3. Queried by ABC, so that ABC doesn't try to set a display brightness value above BrightnessThrottler's limit Bug: 206857086 Bug: 212634465 Test: atest BrightnessThrottlerTest DisplayModeDirectorTest BrightnessLevelPreferenceControllerTest HighBrightnessModeControllerTest AutomaticBrightnessControllerTest Test: Visually observe the SysUI brightness slider and setting screen_brightness adjusting to new maximum brightness values as throttling is toggled. Change-Id: Iab0b773e74f9349fc92949990c96f44d263332aa
This commit is contained in:
@@ -201,6 +201,10 @@ class AutomaticBrightnessController {
|
||||
// Controls High Brightness Mode.
|
||||
private HighBrightnessModeController mHbmController;
|
||||
|
||||
// Throttles (caps) maximum allowed brightness
|
||||
private BrightnessThrottler mBrightnessThrottler;
|
||||
private boolean mIsBrightnessThrottled;
|
||||
|
||||
// Context-sensitive brightness configurations require keeping track of the foreground app's
|
||||
// package name and category, which is done by registering a TaskStackListener to call back to
|
||||
// us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
|
||||
@@ -226,7 +230,7 @@ class AutomaticBrightnessController {
|
||||
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
|
||||
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
|
||||
HysteresisLevels screenBrightnessThresholds, Context context,
|
||||
HighBrightnessModeController hbmController,
|
||||
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
|
||||
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
|
||||
int ambientLightHorizonLong) {
|
||||
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
|
||||
@@ -235,8 +239,8 @@ class AutomaticBrightnessController {
|
||||
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
|
||||
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
|
||||
ambientBrightnessThresholds, screenBrightnessThresholds, context,
|
||||
hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
|
||||
ambientLightHorizonLong
|
||||
hbmController, brightnessThrottler, idleModeBrightnessMapper,
|
||||
ambientLightHorizonShort, ambientLightHorizonLong
|
||||
);
|
||||
}
|
||||
|
||||
@@ -249,7 +253,7 @@ class AutomaticBrightnessController {
|
||||
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
|
||||
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
|
||||
HysteresisLevels screenBrightnessThresholds, Context context,
|
||||
HighBrightnessModeController hbmController,
|
||||
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
|
||||
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
|
||||
int ambientLightHorizonLong) {
|
||||
mInjector = injector;
|
||||
@@ -291,6 +295,7 @@ class AutomaticBrightnessController {
|
||||
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
|
||||
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
|
||||
mHbmController = hbmController;
|
||||
mBrightnessThrottler = brightnessThrottler;
|
||||
mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
|
||||
mIdleModeBrightnessMapper = idleModeBrightnessMapper;
|
||||
// Initialize to active (normal) screen brightness mode
|
||||
@@ -365,6 +370,13 @@ class AutomaticBrightnessController {
|
||||
prepareBrightnessAdjustmentSample();
|
||||
}
|
||||
changed |= setLightSensorEnabled(enable && !dozing);
|
||||
|
||||
if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
|
||||
// Maximum brightness has changed, so recalculate display brightness.
|
||||
mIsBrightnessThrottled = mBrightnessThrottler.isThrottled();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange);
|
||||
}
|
||||
@@ -855,8 +867,11 @@ class AutomaticBrightnessController {
|
||||
|
||||
// Clamps values with float range [0.0-1.0]
|
||||
private float clampScreenBrightness(float value) {
|
||||
return MathUtils.constrain(value,
|
||||
mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
|
||||
final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
|
||||
mBrightnessThrottler.getBrightnessCap());
|
||||
final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
|
||||
mBrightnessThrottler.getBrightnessCap());
|
||||
return MathUtils.constrain(value, minBrightness, maxBrightness);
|
||||
}
|
||||
|
||||
private void prepareBrightnessAdjustmentSample() {
|
||||
|
||||
@@ -974,7 +974,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
|
||||
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
|
||||
ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
|
||||
mHbmController, mIdleModeBrightnessMapper,
|
||||
mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
|
||||
mDisplayDeviceConfig.getAmbientHorizonShort(),
|
||||
mDisplayDeviceConfig.getAmbientHorizonLong());
|
||||
} else {
|
||||
@@ -1344,6 +1344,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
|
||||
}
|
||||
|
||||
// Now that a desired brightness has been calculated, apply brightness throttling. The
|
||||
// dimming and low power transformations that follow can only dim brightness further.
|
||||
//
|
||||
// We didn't do this earlier through brightness clamping because we need to know both
|
||||
// unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
|
||||
// Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
|
||||
// we broadcast this change through setting.
|
||||
final float unthrottledBrightnessState = brightnessState;
|
||||
if (mBrightnessThrottler.isThrottled()) {
|
||||
brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
|
||||
mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
|
||||
if (!mAppliedThrottling) {
|
||||
// Brightness throttling is needed, so do so quickly.
|
||||
// Later, when throttling is removed, we let other mechanisms decide on speed.
|
||||
slowChange = false;
|
||||
updateScreenBrightnessSetting = true;
|
||||
}
|
||||
mAppliedThrottling = true;
|
||||
} else if (mAppliedThrottling) {
|
||||
mAppliedThrottling = false;
|
||||
updateScreenBrightnessSetting = true;
|
||||
}
|
||||
|
||||
if (updateScreenBrightnessSetting) {
|
||||
// Tell the rest of the system about the new brightness in case we had to change it
|
||||
// for things like auto-brightness or high-brightness-mode. Note that we do this
|
||||
@@ -1390,20 +1413,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
mAppliedLowPower = false;
|
||||
}
|
||||
|
||||
// Apply brightness throttling after applying all other transforms
|
||||
final float unthrottledBrightnessState = brightnessState;
|
||||
if (mBrightnessThrottler.isThrottled()) {
|
||||
brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
|
||||
mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
|
||||
if (!mAppliedThrottling) {
|
||||
slowChange = false;
|
||||
}
|
||||
mAppliedThrottling = true;
|
||||
} else if (mAppliedThrottling) {
|
||||
slowChange = false;
|
||||
mAppliedThrottling = false;
|
||||
}
|
||||
|
||||
// The current brightness to use has been calculated at this point, and HbmController should
|
||||
// be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
|
||||
// here instead of having HbmController listen to the brightness setting because certain
|
||||
@@ -1653,6 +1662,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
|
||||
private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
|
||||
synchronized (mCachedBrightnessInfo) {
|
||||
final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
|
||||
mBrightnessThrottler.getBrightnessCap());
|
||||
final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
|
||||
mBrightnessThrottler.getBrightnessCap());
|
||||
boolean changed = false;
|
||||
|
||||
changed |=
|
||||
@@ -1663,10 +1676,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
adjustedBrightness);
|
||||
changed |=
|
||||
mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
|
||||
mHbmController.getCurrentBrightnessMin());
|
||||
minBrightness);
|
||||
changed |=
|
||||
mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
|
||||
mHbmController.getCurrentBrightnessMax());
|
||||
maxBrightness);
|
||||
changed |=
|
||||
mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
|
||||
mHbmController.getHighBrightnessMode());
|
||||
|
||||
@@ -81,6 +81,7 @@ public class AutomaticBrightnessControllerTest {
|
||||
@Mock HysteresisLevels mScreenBrightnessThresholds;
|
||||
@Mock Handler mNoOpHandler;
|
||||
@Mock HighBrightnessModeController mHbmController;
|
||||
@Mock BrightnessThrottler mBrightnessThrottler;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -128,12 +129,15 @@ public class AutomaticBrightnessControllerTest {
|
||||
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
|
||||
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
|
||||
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
|
||||
mContext, mHbmController, mIdleBrightnessMappingStrategy,
|
||||
mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
|
||||
AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
|
||||
);
|
||||
|
||||
when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
|
||||
when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT);
|
||||
// Disable brightness throttling by default. Individual tests can enable it as needed.
|
||||
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
|
||||
when(mBrightnessThrottler.isThrottled()).thenReturn(false);
|
||||
|
||||
// Configure the brightness controller and grab an instance of the sensor listener,
|
||||
// through which we can deliver fake (for test) sensor values.
|
||||
@@ -420,4 +424,47 @@ public class AutomaticBrightnessControllerTest {
|
||||
assertEquals(600f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
|
||||
assertEquals(250f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessGetsThrottled() throws Exception {
|
||||
Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
|
||||
mController = setupController(lightSensor);
|
||||
|
||||
ArgumentCaptor<SensorEventListener> listenerCaptor =
|
||||
ArgumentCaptor.forClass(SensorEventListener.class);
|
||||
verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
|
||||
eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
|
||||
SensorEventListener listener = listenerCaptor.getValue();
|
||||
|
||||
// Set up system to return max brightness at 100 lux
|
||||
final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT;
|
||||
final float lux = 100.0f;
|
||||
when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux))
|
||||
.thenReturn(lux);
|
||||
when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux))
|
||||
.thenReturn(lux);
|
||||
when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
|
||||
.thenReturn(normalizedBrightness);
|
||||
|
||||
// Sensor reads 100 lux. We should get max brightness.
|
||||
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux));
|
||||
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
|
||||
|
||||
// Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
|
||||
final float throttledBrightness = 0.123f;
|
||||
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness);
|
||||
when(mBrightnessThrottler.isThrottled()).thenReturn(true);
|
||||
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
|
||||
BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
|
||||
0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
|
||||
assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
|
||||
|
||||
// Remove throttling and notify ABC again
|
||||
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
|
||||
when(mBrightnessThrottler.isThrottled()).thenReturn(false);
|
||||
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
|
||||
BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
|
||||
0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
|
||||
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user