Merge "Add ambientLightHorizon to DDC"

This commit is contained in:
Fiona Campbell
2022-01-17 13:29:23 +00:00
committed by Android (Google) Code Review
6 changed files with 215 additions and 31 deletions

View File

@@ -74,13 +74,6 @@ class AutomaticBrightnessController {
private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
private static final int MSG_RUN_UPDATE = 6;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
// Callbacks for requesting updates to the display's power state
private final Callbacks mCallbacks;
@@ -125,8 +118,10 @@ class AutomaticBrightnessController {
// and only then decide whether to change brightness.
private final boolean mResetAmbientLuxAfterWarmUpConfig;
// Period of time in which to consider light samples in milliseconds.
private final int mAmbientLightHorizon;
// Period of time in which to consider light samples for a short/long-term estimate of ambient
// light in milliseconds.
private final int mAmbientLightHorizonLong;
private final int mAmbientLightHorizonShort;
// The intercept used for the weighting calculation. This is used in order to keep all possible
// weighting values positive.
@@ -220,6 +215,7 @@ class AutomaticBrightnessController {
private Context mContext;
private int mState = AUTO_BRIGHTNESS_DISABLED;
private Clock mClock;
private final Injector mInjector;
AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -231,14 +227,16 @@ class AutomaticBrightnessController {
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
HighBrightnessModeController hbmController,
BrightnessMappingStrategy idleModeBrightnessMapper) {
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
interactiveModeBrightnessMapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds, context,
hbmController, idleModeBrightnessMapper
hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
ambientLightHorizonLong
);
}
@@ -252,8 +250,10 @@ class AutomaticBrightnessController {
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
HighBrightnessModeController hbmController,
BrightnessMappingStrategy idleModeBrightnessMapper) {
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
mInjector = injector;
mClock = injector.createClock();
mContext = context;
mCallbacks = callbacks;
mSensorManager = sensorManager;
@@ -268,15 +268,16 @@ class AutomaticBrightnessController {
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
mAmbientLightHorizonLong = ambientLightHorizonLong;
mAmbientLightHorizonShort = ambientLightHorizonShort;
mWeightingIntercept = ambientLightHorizonLong;
mAmbientBrightnessThresholds = ambientBrightnessThresholds;
mScreenBrightnessThresholds = screenBrightnessThresholds;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer =
new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = lightSensor;
@@ -397,6 +398,11 @@ class AutomaticBrightnessController {
mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
}
@VisibleForTesting
float getAmbientLux() {
return mAmbientLux;
}
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
@@ -476,7 +482,8 @@ class AutomaticBrightnessController {
pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
pw.println(" mWeightingIntercept=" + mWeightingIntercept);
pw.println();
@@ -545,7 +552,7 @@ class AutomaticBrightnessController {
if (enable) {
if (!mLightSensorEnabled) {
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mLightSensorEnableTime = mClock.uptimeMillis();
mCurrentLightSensorRate = mInitialLightSensorRate;
registerForegroundAppUpdater();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
@@ -580,7 +587,7 @@ class AutomaticBrightnessController {
private void applyLightSensorMeasurement(long time, float lux) {
mRecentLightSamples++;
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
mAmbientLightRingBuffer.push(time, lux);
// Remember this sample value.
@@ -721,8 +728,8 @@ class AutomaticBrightnessController {
}
private void updateAmbientLux() {
long time = SystemClock.uptimeMillis();
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
long time = mClock.uptimeMillis();
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
updateAmbientLux(time);
}
@@ -742,7 +749,7 @@ class AutomaticBrightnessController {
timeWhenSensorWarmedUp);
return;
}
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
setAmbientLux(calculateAmbientLux(time, mAmbientLightHorizonShort));
mAmbientLuxValid = true;
if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Initializing: " +
@@ -762,8 +769,8 @@ class AutomaticBrightnessController {
// proposed ambient light value since the slow value might be sufficiently far enough away
// from the fast value to cause a recalculation while its actually just converging on
// the fast value still.
float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
float slowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
float fastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
if ((slowAmbientLux >= mAmbientBrighteningThreshold
&& fastAmbientLux >= mAmbientBrighteningThreshold
@@ -1044,7 +1051,7 @@ class AutomaticBrightnessController {
@Override
public void onSensorChanged(SensorEvent event) {
if (mLightSensorEnabled) {
final long time = SystemClock.uptimeMillis();
final long time = mClock.uptimeMillis();
final float lux = event.values[0];
handleLightSensorEvent(time, lux);
}
@@ -1070,6 +1077,15 @@ class AutomaticBrightnessController {
void updateBrightness();
}
/** Functional interface for providing time. */
@VisibleForTesting
interface Clock {
/**
* Returns current time in milliseconds since boot, not counting time spent in deep sleep.
*/
long uptimeMillis();
}
/**
* A ring buffer of ambient light measurements sorted by time.
*
@@ -1089,14 +1105,16 @@ class AutomaticBrightnessController {
private int mStart;
private int mEnd;
private int mCount;
Clock mClock;
public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock) {
if (lightSensorRate <= 0) {
throw new IllegalArgumentException("lightSensorRate must be above 0");
}
mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
mRingLux = new float[mCapacity];
mRingTime = new long[mCapacity];
mClock = clock;
}
public float getLux(int index) {
@@ -1181,7 +1199,7 @@ class AutomaticBrightnessController {
StringBuilder buf = new StringBuilder();
buf.append('[');
for (int i = 0; i < mCount; i++) {
final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
if (i != 0) {
buf.append(", ");
}
@@ -1210,5 +1228,9 @@ class AutomaticBrightnessController {
public Handler getBackgroundThreadHandler() {
return BackgroundThread.getHandler();
}
Clock createClock() {
return SystemClock::uptimeMillis;
}
}
}

View File

@@ -52,6 +52,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -86,6 +87,13 @@ public class DisplayDeviceConfig {
private static final float NITS_INVALID = -1;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
private final Context mContext;
// The details of the ambient light sensor associated with this display.
@@ -120,6 +128,8 @@ public class DisplayDeviceConfig {
private float mBrightnessRampFastIncrease = Float.NaN;
private float mBrightnessRampSlowDecrease = Float.NaN;
private float mBrightnessRampSlowIncrease = Float.NaN;
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
@@ -346,6 +356,14 @@ public class DisplayDeviceConfig {
return mBrightnessRampSlowIncrease;
}
public int getAmbientHorizonLong() {
return mAmbientHorizonLong;
}
public int getAmbientHorizonShort() {
return mAmbientHorizonShort;
}
SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
@@ -405,6 +423,8 @@ public class DisplayDeviceConfig {
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ ", mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -461,6 +481,7 @@ public class DisplayDeviceConfig {
loadBrightnessRamps(config);
loadAmbientLightSensorFromDdc(config);
loadProxSensorFromDdc(config);
loadAmbientHorizonFromDdc(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -869,6 +890,17 @@ public class DisplayDeviceConfig {
}
}
private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
if (configLongHorizon != null) {
mAmbientHorizonLong = configLongHorizon.intValue();
}
final BigInteger configShortHorizon = config.getAmbientLightHorizonShort();
if (configShortHorizon != null) {
mAmbientHorizonShort = configShortHorizon.intValue();
}
}
static class SensorData {
public String type;
public String name;

View File

@@ -957,7 +957,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
mHbmController, mIdleModeBrightnessMapper);
mHbmController, mIdleModeBrightnessMapper,
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong());
} else {
mUseSoftwareAutoBrightnessConfig = false;
}

View File

@@ -58,6 +58,15 @@
<xs:element type="sensorDetails" name="proxSensor">
<xs:annotation name="final"/>
</xs:element>
<!-- Length of the ambient light horizon used to calculate the long & short term
estimates of ambient light in milliseconds.-->
<xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonLong">
<xs:annotation name="final"/>
</xs:element>
<xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonShort">
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

View File

@@ -18,6 +18,8 @@ package com.android.server.display.config {
public class DisplayConfiguration {
ctor public DisplayConfiguration();
method public final java.math.BigInteger getAmbientLightHorizonLong();
method public final java.math.BigInteger getAmbientLightHorizonShort();
method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
@@ -29,6 +31,8 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
method public final void setAmbientLightHorizonShort(java.math.BigInteger);
method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);

View File

@@ -19,6 +19,7 @@ package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
@@ -34,18 +35,22 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.testutils.OffsettableClock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@@ -61,7 +66,11 @@ public class AutomaticBrightnessControllerTest {
private static final float DOZE_SCALE_FACTOR = 0.0f;
private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000;
private static final float EPSILON = 0.001f;
private OffsettableClock mClock = new OffsettableClock();
private TestLooper mTestLooper;
private Context mContext;
private AutomaticBrightnessController mController;
@@ -91,21 +100,36 @@ public class AutomaticBrightnessControllerTest {
}
}
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
}
private AutomaticBrightnessController setupController(Sensor lightSensor) {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
AutomaticBrightnessController controller = new AutomaticBrightnessController(
new AutomaticBrightnessController.Injector() {
@Override
public Handler getBackgroundThreadHandler() {
return mNoOpHandler;
}
},
() -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
@Override
AutomaticBrightnessController.Clock createClock() {
return mClock::now;
}
}, // pass in test looper instead, pass in offsetable clock
() -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
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, mIdleBrightnessMappingStrategy,
AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
);
when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -276,4 +300,95 @@ public class AutomaticBrightnessControllerTest {
// Ensure we use the correct mapping strategy
verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
}
@Test
public void testAmbientLightHorizon() throws Exception {
// create abc
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();
long increment = 500;
// set autobrightness to low
// t = 0
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
// t = 500
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
// t = 1000
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 1500
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 2000
// ensure that our reading is at 0.
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 2500
// first 10000 lux sensor event reading
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 3000
// lux reading should still not yet be 10000.
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 3500
mClock.fastForward(increment);
// lux has been high (10000) for 1000ms.
// lux reading should be 10000
// short horizon (ambient lux) is high, long horizon is still not high
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 4000
// stay high
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 4500
Mockito.clearInvocations(mBrightnessMappingStrategy);
mClock.fastForward(increment);
// short horizon is high, long horizon is high too
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 5000
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 5500
mClock.fastForward(increment);
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 6000
mClock.fastForward(increment);
// ambient lux goes to 0
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
}
}