AutomaticBrightnessControllerpublic class AutomaticBrightnessController extends Object
Fields Summary |
---|
private static final String | TAG | private static final boolean | DEBUG | private static final boolean | DEBUG_PRETEND_LIGHT_SENSOR_ABSENT | private static final boolean | USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT | private static final float | SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA | private static final int | LIGHT_SENSOR_RATE_MILLIS | private static final int | AMBIENT_LIGHT_HORIZON | private static final long | BRIGHTENING_LIGHT_DEBOUNCE | private static final long | DARKENING_LIGHT_DEBOUNCE | private static final float | BRIGHTENING_LIGHT_HYSTERESIS | private static final float | DARKENING_LIGHT_HYSTERESIS | private static final int | WEIGHTING_INTERCEPT | private static final long | AMBIENT_LIGHT_PREDICTION_TIME_MILLIS | private static final boolean | USE_TWILIGHT_ADJUSTMENT | private static final float | TWILIGHT_ADJUSTMENT_MAX_GAMMA | private static final long | TWILIGHT_ADJUSTMENT_TIME | private static final int | MSG_UPDATE_AMBIENT_LUX | private final Callbacks | mCallbacks | private final android.hardware.SensorManager | mSensorManager | private final android.hardware.Sensor | mLightSensor | private final com.android.server.twilight.TwilightManager | mTwilight | private final android.util.Spline | mScreenAutoBrightnessSpline | private final int | mScreenBrightnessRangeMinimum | private final int | mScreenBrightnessRangeMaximum | private final float | mDozeScaleFactor | private int | mLightSensorWarmUpTimeConfig | private boolean | mLightSensorEnabled | private long | mLightSensorEnableTime | private float | mAmbientLux | private boolean | mAmbientLuxValid | private float | mBrighteningLuxThreshold | private float | mDarkeningLuxThreshold | private float | mLastObservedLux | private long | mLastObservedLuxTime | private int | mRecentLightSamples | private AmbientLightRingBuffer | mAmbientLightRingBuffer | private AutomaticBrightnessHandler | mHandler | private int | mScreenAutoBrightness | private float | mScreenAutoBrightnessAdjustment | private float | mLastScreenAutoBrightnessGamma | private boolean | mDozing | private final android.hardware.SensorEventListener | mLightSensorListener | private final com.android.server.twilight.TwilightListener | mTwilightListener |
Constructors Summary |
---|
public AutomaticBrightnessController(Callbacks callbacks, android.os.Looper looper, android.hardware.SensorManager sensorManager, android.util.Spline autoBrightnessSpline, int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor)
mCallbacks = callbacks;
mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
mScreenAutoBrightnessSpline = autoBrightnessSpline;
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
mDozeScaleFactor = dozeScaleFactor;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer = new AmbientLightRingBuffer();
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
if (USE_TWILIGHT_ADJUSTMENT) {
mTwilight.registerListener(mTwilightListener, mHandler);
}
|
Methods Summary |
---|
private void | applyLightSensorMeasurement(long time, float lux)
mRecentLightSamples++;
mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
mAmbientLightRingBuffer.push(time, lux);
// Remember this sample value.
mLastObservedLux = lux;
mLastObservedLuxTime = time;
| private float | calculateAmbientLux(long now)
final int N = mAmbientLightRingBuffer.size();
if (N == 0) {
Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
return -1;
}
float sum = 0;
float totalWeight = 0;
long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
for (int i = N - 1; i >= 0; i--) {
long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
if (DEBUG) {
Slog.d(TAG, "calculateAmbientLux: [" +
(startTime) + ", " +
(endTime) + "]: lux=" + lux + ", weight=" + weight);
}
totalWeight += weight;
sum += mAmbientLightRingBuffer.getLux(i) * weight;
endTime = startTime;
}
if (DEBUG) {
Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
", newAmbientLux=" + (sum / totalWeight));
}
return sum / totalWeight;
| private static float | calculateWeight(long startDelta, long endDelta)
return weightIntegral(endDelta) - weightIntegral(startDelta);
| private int | clampScreenBrightness(int value)
return MathUtils.constrain(value,
mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
| public void | configure(boolean enable, float adjustment, boolean dozing)
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
// and hold onto the last computed screen auto brightness. We save the dozing flag for
// debugging purposes.
mDozing = dozing;
boolean changed = setLightSensorEnabled(enable && !dozing);
changed |= setScreenAutoBrightnessAdjustment(adjustment);
if (changed) {
updateAutoBrightness(false /*sendUpdate*/);
}
| public void | dump(java.io.PrintWriter pw)
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
pw.println();
pw.println("Automatic Brightness Controller State:");
pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
pw.println(" mLastObservedLux=" + mLastObservedLux);
pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
pw.println(" mRecentLightSamples=" + mRecentLightSamples);
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
pw.println(" mDozing=" + mDozing);
| public int | getAutomaticScreenBrightness()
if (mDozing) {
return (int) (mScreenAutoBrightness * mDozeScaleFactor);
}
return mScreenAutoBrightness;
| private static float | getTwilightGamma(long now, long lastSunset, long nextSunrise)
if (lastSunset < 0 || nextSunrise < 0
|| now < lastSunset || now > nextSunrise) {
return 1.0f;
}
if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
(float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
}
if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
(float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
}
return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
| private void | handleLightSensorEvent(long time, float lux)
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
applyLightSensorMeasurement(time, lux);
updateAmbientLux(time);
| private long | nextAmbientLightBrighteningTransition(long time)
final int N = mAmbientLightRingBuffer.size();
long earliestValidTime = time;
for (int i = N - 1; i >= 0; i--) {
if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
break;
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
return earliestValidTime + BRIGHTENING_LIGHT_DEBOUNCE;
| private long | nextAmbientLightDarkeningTransition(long time)
final int N = mAmbientLightRingBuffer.size();
long earliestValidTime = time;
for (int i = N - 1; i >= 0; i--) {
if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
break;
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
return earliestValidTime + DARKENING_LIGHT_DEBOUNCE;
| private void | setAmbientLux(float lux)
mAmbientLux = lux;
mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
| private boolean | setLightSensorEnabled(boolean enable)
if (enable) {
if (!mLightSensorEnabled) {
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
return true;
}
} else {
if (mLightSensorEnabled) {
mLightSensorEnabled = false;
mAmbientLuxValid = false;
mRecentLightSamples = 0;
mAmbientLightRingBuffer.clear();
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
mSensorManager.unregisterListener(mLightSensorListener);
}
}
return false;
| private boolean | setScreenAutoBrightnessAdjustment(float adjustment)
if (adjustment != mScreenAutoBrightnessAdjustment) {
mScreenAutoBrightnessAdjustment = adjustment;
return true;
}
return false;
| private void | updateAmbientLux()
long time = SystemClock.uptimeMillis();
mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
updateAmbientLux(time);
| private void | updateAmbientLux(long time)
// If the light sensor was just turned on then immediately update our initial
// estimate of the current ambient light level.
if (!mAmbientLuxValid) {
final long timeWhenSensorWarmedUp =
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
+ "time=" + time
+ ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
}
mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
timeWhenSensorWarmedUp);
return;
}
setAmbientLux(calculateAmbientLux(time));
mAmbientLuxValid = true;
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: Initializing: "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ ", mAmbientLux=" + mAmbientLux);
}
updateAutoBrightness(true);
}
long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
float ambientLux = calculateAmbientLux(time);
if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
|| ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
setAmbientLux(ambientLux);
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: "
+ ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
+ ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ ", mAmbientLux=" + mAmbientLux);
}
updateAutoBrightness(true);
nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
}
long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
// If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
// exceed the necessary threshold, then it's possible we'll get a transition time prior to
// now. Rather than continually checking to see whether the weighted lux exceeds the
// threshold, schedule an update for when we'd normally expect another light sample, which
// should be enough time to decide whether we should actually transition to the new
// weighted ambient lux or not.
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + LIGHT_SENSOR_RATE_MILLIS;
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
+ nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
| private void | updateAutoBrightness(boolean sendUpdate)
if (!mAmbientLuxValid) {
return;
}
float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
float gamma = 1.0f;
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
&& mScreenAutoBrightnessAdjustment != 0.0f) {
final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
gamma *= adjGamma;
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
}
}
if (USE_TWILIGHT_ADJUSTMENT) {
TwilightState state = mTwilight.getCurrentState();
if (state != null && state.isNight()) {
final long now = System.currentTimeMillis();
final float earlyGamma =
getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
final float lateGamma =
getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
gamma *= earlyGamma * lateGamma;
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
+ ", lateGamma=" + lateGamma);
}
}
}
if (gamma != 1.0f) {
final float in = value;
value = MathUtils.pow(value, gamma);
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
+ ", in=" + in + ", out=" + value);
}
}
int newScreenAutoBrightness =
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
if (mScreenAutoBrightness != newScreenAutoBrightness) {
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
+ mScreenAutoBrightness + ", newScreenAutoBrightness="
+ newScreenAutoBrightness);
}
mScreenAutoBrightness = newScreenAutoBrightness;
mLastScreenAutoBrightnessGamma = gamma;
if (sendUpdate) {
mCallbacks.updateBrightness();
}
}
| private static float | weightIntegral(long x)
return x * (x * 0.5f + WEIGHTING_INTERCEPT);
|
|