Merge "Reduce auto-brightness jitter." into jb-mr1-dev

This commit is contained in:
Jeff Brown
2012-10-18 23:22:14 -07:00
committed by Android (Google) Code Review
2 changed files with 135 additions and 145 deletions

View File

@@ -128,28 +128,33 @@ final class DisplayPowerController {
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
// Light sensor event rate in microseconds. // Light sensor event rate in microseconds.
private static final int LIGHT_SENSOR_RATE = 1000000; private static final int LIGHT_SENSOR_RATE = 500 * 1000;
// Brightness animation ramp rate in brightness units per second. // Brightness animation ramp rate in brightness units per second.
private static final int BRIGHTNESS_RAMP_RATE_FAST = 200; private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40; private static final int BRIGHTNESS_RAMP_RATE_SLOW = 30;
// Filter time constant in milliseconds for computing a moving // IIR filter time constants in milliseconds for computing two moving averages of
// average of light samples. Different constants are used // the light samples. One is a long-term average and the other is a short-term average.
// to calculate the average light level when adapting to brighter or // We can use these filters to assess trends in ambient brightness.
// dimmer environments. // The short term average gives us a filtered but relatively low latency measurement.
// This parameter only controls the filtering of light samples. // The long term average informs us about the overall trend.
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 600; private static final long SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 1000;
private static final long DIMMING_LIGHT_TIME_CONSTANT = 4000; private static final long LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 8000;
// Stability requirements in milliseconds for accepting a new brightness // Stability requirements in milliseconds for accepting a new brightness
// level. This is used for debouncing the light sensor. Different constants // level. This is used for debouncing the light sensor. Different constants
// are used to debounce the light sensor when adapting to brighter or dimmer // are used to debounce the light sensor when adapting to brighter or darker environments.
// environments.
// This parameter controls how quickly brightness changes occur in response to // This parameter controls how quickly brightness changes occur in response to
// an observed change in light level. // an observed change in light level following a previous change in the opposite direction.
private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500; private static final long BRIGHTENING_LIGHT_DEBOUNCE = 5000;
private static final long DIMMING_LIGHT_DEBOUNCE = 10000; private static final long DARKENING_LIGHT_DEBOUNCE = 15000;
// Hysteresis constraints for brightening or darkening.
// The recent lux must have changed by at least this fraction relative to the
// current ambient lux before a change will be considered.
private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
private final Object mLock = new Object(); private final Object mLock = new Object();
@@ -284,39 +289,28 @@ final class DisplayPowerController {
// The time when the light sensor was enabled. // The time when the light sensor was enabled.
private long mLightSensorEnableTime; private long mLightSensorEnableTime;
// The currently accepted average light sensor value. // The currently accepted nominal ambient light level.
private float mLightMeasurement; private float mAmbientLux;
// True if the light sensor measurement is valid. // True if mAmbientLux holds a valid value.
private boolean mLightMeasurementValid; private boolean mAmbientLuxValid;
// The number of light sensor samples that have been collected since the // The time when the ambient lux was last brightened or darkened.
// last time a light sensor reading was accepted. private long mLastAmbientBrightenTime;
private int mRecentLightSamples; private long mLastAmbientDarkenTime;
// The moving average of recent light sensor values.
private float mRecentLightAverage;
// True if recent light samples are getting brighter than the previous
// stable light measurement.
private boolean mRecentLightBrightening;
// The time constant to use for filtering based on whether the
// light appears to be brightening or dimming.
private long mRecentLightTimeConstant;
// The most recent light sample. // The most recent light sample.
private float mLastLightSample; private float mLastObservedLux;
// The time of the most light recent sample. // The time of the most light recent sample.
private long mLastLightSampleTime; private long mLastObservedLuxTime;
// The time when we accumulated the first recent light sample into mRecentLightSamples. // The number of light samples collected since the light sensor was enabled.
private long mFirstRecentLightSampleTime; private int mRecentLightSamples;
// The upcoming debounce light sensor time. // The long-term and short-term filtered light measurements.
// This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1. private float mRecentShortTermAverageLux;
private long mPendingLightSensorDebounceTime; private float mRecentLongTermAverageLux;
// The screen brightness level that has been chosen by the auto-brightness // The screen brightness level that has been chosen by the auto-brightness
// algorithm. The actual brightness should ramp towards this value. // algorithm. The actual brightness should ramp towards this value.
@@ -873,7 +867,8 @@ final class DisplayPowerController {
} else { } else {
if (mLightSensorEnabled) { if (mLightSensorEnabled) {
mLightSensorEnabled = false; mLightSensorEnabled = false;
mLightMeasurementValid = false; mAmbientLuxValid = false;
mRecentLightSamples = 0;
mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
mSensorManager.unregisterListener(mLightSensorListener); mSensorManager.unregisterListener(mLightSensorListener);
} }
@@ -884,114 +879,99 @@ final class DisplayPowerController {
} }
private void handleLightSensorEvent(long time, float lux) { private void handleLightSensorEvent(long time, float lux) {
// Take the first few readings during the warm-up period and apply them // Update our filters.
// immediately without debouncing.
if (!mLightMeasurementValid
|| (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
mLightMeasurement = lux;
mLightMeasurementValid = true;
mRecentLightSamples = 0;
updateAutoBrightness(true);
}
// Update our moving average.
if (lux != mLightMeasurement && (mRecentLightSamples == 0
|| (lux < mLightMeasurement && mRecentLightBrightening)
|| (lux > mLightMeasurement && !mRecentLightBrightening))) {
// If the newest light sample doesn't seem to be going in the
// same general direction as recent samples, then start over.
setRecentLight(time, lux, lux > mLightMeasurement);
} else if (mRecentLightSamples >= 1) {
// Add the newest light sample to the moving average.
accumulateRecentLight(time, lux);
}
if (DEBUG) {
Slog.d(TAG, "handleLightSensorEvent: lux=" + lux
+ ", mLightMeasurementValid=" + mLightMeasurementValid
+ ", mLightMeasurement=" + mLightMeasurement
+ ", mRecentLightSamples=" + mRecentLightSamples
+ ", mRecentLightAverage=" + mRecentLightAverage
+ ", mRecentLightBrightening=" + mRecentLightBrightening
+ ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
+ ", mFirstRecentLightSampleTime="
+ TimeUtils.formatUptime(mFirstRecentLightSampleTime)
+ ", mPendingLightSensorDebounceTime="
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
}
// Debounce.
mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
debounceLightSensor();
}
private void setRecentLight(long time, float lux, boolean brightening) {
mRecentLightBrightening = brightening;
mRecentLightTimeConstant = brightening ?
BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT;
mRecentLightSamples = 1;
mRecentLightAverage = lux;
mLastLightSample = lux;
mLastLightSampleTime = time;
mFirstRecentLightSampleTime = time;
mPendingLightSensorDebounceTime = time + (brightening ?
BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE);
}
private void accumulateRecentLight(long time, float lux) {
final long timeDelta = time - mLastLightSampleTime;
mRecentLightSamples += 1; mRecentLightSamples += 1;
mRecentLightAverage += (lux - mRecentLightAverage) * if (mRecentLightSamples == 1) {
timeDelta / (mRecentLightTimeConstant + timeDelta); mRecentShortTermAverageLux = lux;
mLastLightSample = lux; mRecentLongTermAverageLux = lux;
mLastLightSampleTime = time; } else {
final long timeDelta = time - mLastObservedLuxTime;
mRecentShortTermAverageLux += (lux - mRecentShortTermAverageLux)
* timeDelta / (SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
mRecentLongTermAverageLux += (lux - mRecentLongTermAverageLux)
* timeDelta / (LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
}
// Remember this sample value.
mLastObservedLux = lux;
mLastObservedLuxTime = time;
// Update the ambient lux level.
mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
updateAmbientLux(time);
} }
private void debounceLightSensor() { private void updateAmbientLux(long time) {
if (mLightMeasurementValid && mRecentLightSamples >= 1) { // If the light sensor was just turned on then immediately update our initial
final long now = SystemClock.uptimeMillis(); // estimate of the current ambient light level.
if (mPendingLightSensorDebounceTime <= now) { if (!mAmbientLuxValid
accumulateRecentLight(now, mLastLightSample); || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
mLightMeasurement = mRecentLightAverage; if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: Initializing, "
+ "mAmbientLux=" + (mAmbientLuxValid ? mAmbientLux : -1)
+ ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
+ ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
}
mAmbientLux = mRecentShortTermAverageLux;
mAmbientLuxValid = true;
mLastAmbientBrightenTime = time;
mLastAmbientDarkenTime = time;
updateAutoBrightness(true);
return;
}
// Determine whether the ambient environment appears to be brightening.
float minAmbientLux = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
if (mRecentShortTermAverageLux > minAmbientLux
&& mRecentLongTermAverageLux > minAmbientLux) {
long debounceTime = mLastAmbientDarkenTime + BRIGHTENING_LIGHT_DEBOUNCE;
if (time >= debounceTime) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "debounceLightSensor: Accepted new measurement " Slog.d(TAG, "updateAmbientLux: Brightened: "
+ mLightMeasurement + " after " + "mAmbientLux=" + mAmbientLux
+ (now - mFirstRecentLightSampleTime) + " ms based on " + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
+ mRecentLightSamples + " recent samples."); + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
} }
mLastAmbientBrightenTime = time;
mAmbientLux = mRecentShortTermAverageLux;
updateAutoBrightness(true); updateAutoBrightness(true);
// Now that we have debounced the light sensor data, we have the
// option of either leaving the sensor in a debounced state or
// restarting the debounce cycle by setting mRecentLightSamples to 0.
//
// If we leave the sensor debounced, then new average light measurements
// may be accepted immediately as long as they are trending in the same
// direction as they were before. If the measurements start
// jittering or trending in the opposite direction then the debounce
// cycle will automatically be restarted. The benefit is that the
// auto-brightness control can be more responsive to changes over a
// broad range.
//
// For now, we choose to be more responsive and leave the following line
// commented out.
//
// mRecentLightSamples = 0;
} else { } else {
Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED); mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
msg.setAsynchronous(true); }
mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime); return;
}
// Determine whether the ambient environment appears to be darkening.
float maxAmbientLux = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
if (mRecentShortTermAverageLux < maxAmbientLux
&& mRecentLongTermAverageLux < maxAmbientLux) {
long debounceTime = mLastAmbientBrightenTime + DARKENING_LIGHT_DEBOUNCE;
if (time >= debounceTime) {
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: Darkened: "
+ "mAmbientLux=" + mAmbientLux
+ ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
+ ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
}
mLastAmbientDarkenTime = time;
mAmbientLux = mRecentShortTermAverageLux;
updateAutoBrightness(true);
} else {
mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
} }
} }
} }
private void debounceLightSensor() {
updateAmbientLux(SystemClock.uptimeMillis());
}
private void updateAutoBrightness(boolean sendUpdate) { private void updateAutoBrightness(boolean sendUpdate) {
if (!mLightMeasurementValid) { if (!mAmbientLuxValid) {
return; return;
} }
float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement); float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
float gamma = 1.0f; float gamma = 1.0f;
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
@@ -1031,7 +1011,7 @@ final class DisplayPowerController {
} }
int newScreenAutoBrightness = clampScreenBrightness( int newScreenAutoBrightness = clampScreenBrightness(
(int)Math.round(value * PowerManager.BRIGHTNESS_ON)); Math.round(value * PowerManager.BRIGHTNESS_ON));
if (mScreenAutoBrightness != newScreenAutoBrightness) { if (mScreenAutoBrightness != newScreenAutoBrightness) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
@@ -1152,19 +1132,18 @@ final class DisplayPowerController {
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" pw.println(" mLightSensorEnableTime="
+ TimeUtils.formatUptime(mLightSensorEnableTime)); + TimeUtils.formatUptime(mLightSensorEnableTime));
pw.println(" mLightMeasurement=" + mLightMeasurement); pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mLightMeasurementValid=" + mLightMeasurementValid); pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
pw.println(" mLastLightSample=" + mLastLightSample); pw.println(" mLastAmbientBrightenTime="
pw.println(" mLastLightSampleTime=" + TimeUtils.formatUptime(mLastAmbientBrightenTime));
+ TimeUtils.formatUptime(mLastLightSampleTime)); pw.println(" mLastAmbientDimTime="
+ TimeUtils.formatUptime(mLastAmbientDarkenTime));
pw.println(" mLastObservedLux=" + mLastObservedLux);
pw.println(" mLastObservedLuxTime="
+ TimeUtils.formatUptime(mLastObservedLuxTime));
pw.println(" mRecentLightSamples=" + mRecentLightSamples); pw.println(" mRecentLightSamples=" + mRecentLightSamples);
pw.println(" mRecentLightAverage=" + mRecentLightAverage); pw.println(" mRecentShortTermAverageLux=" + mRecentShortTermAverageLux);
pw.println(" mRecentLightBrightening=" + mRecentLightBrightening); pw.println(" mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant);
pw.println(" mFirstRecentLightSampleTime="
+ TimeUtils.formatUptime(mFirstRecentLightSampleTime));
pw.println(" mPendingLightSensorDebounceTime="
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness); pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);

View File

@@ -16,6 +16,8 @@
package com.android.server.power; package com.android.server.power;
import android.util.Slog;
import com.android.server.LightsService; import com.android.server.LightsService;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -27,6 +29,9 @@ import java.util.concurrent.Executor;
* setting the backlight brightness is especially slow. * setting the backlight brightness is especially slow.
*/ */
final class PhotonicModulator { final class PhotonicModulator {
private static final String TAG = "PhotonicModulator";
private static final boolean DEBUG = false;
private static final int UNKNOWN_LIGHT_VALUE = -1; private static final int UNKNOWN_LIGHT_VALUE = -1;
private final Object mLock = new Object(); private final Object mLock = new Object();
@@ -58,6 +63,9 @@ final class PhotonicModulator {
synchronized (mLock) { synchronized (mLock) {
if (lightValue != mPendingLightValue) { if (lightValue != mPendingLightValue) {
mPendingLightValue = lightValue; mPendingLightValue = lightValue;
if (DEBUG) {
Slog.d(TAG, "Enqueuing request to change brightness to " + lightValue);
}
if (!mPendingChange) { if (!mPendingChange) {
mPendingChange = true; mPendingChange = true;
mSuspendBlocker.acquire(); mSuspendBlocker.acquire();
@@ -91,6 +99,9 @@ final class PhotonicModulator {
} }
mActualLightValue = newLightValue; mActualLightValue = newLightValue;
} }
if (DEBUG) {
Slog.d(TAG, "Setting brightness to " + newLightValue);
}
mLight.setBrightness(newLightValue); mLight.setBrightness(newLightValue);
} }
} }