diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-10-18 23:22:14 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-10-18 23:23:57 -0700 |
commit | 1d2b445d7431420b7cfbd9ef3fe4e326e121eeae (patch) | |
tree | e363717ec922a62371117f6c5eafbf6e39515c00 /services | |
parent | 5831159b8d4cf85fa46e2c4cb8e7d9f750014d6f (diff) | |
parent | 4f0e969eabc336f6dfa31c6218efc1b3dec55139 (diff) | |
download | frameworks_base-1d2b445d7431420b7cfbd9ef3fe4e326e121eeae.zip frameworks_base-1d2b445d7431420b7cfbd9ef3fe4e326e121eeae.tar.gz frameworks_base-1d2b445d7431420b7cfbd9ef3fe4e326e121eeae.tar.bz2 |
Merge "Reduce auto-brightness jitter." into jb-mr1-dev
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/power/DisplayPowerController.java | 261 | ||||
-rw-r--r-- | services/java/com/android/server/power/PhotonicModulator.java | 11 |
2 files changed, 131 insertions, 141 deletions
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 1561dba..661b949 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -128,28 +128,33 @@ final class DisplayPowerController { private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; // 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. 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 - // average of light samples. Different constants are used - // to calculate the average light level when adapting to brighter or - // dimmer environments. - // This parameter only controls the filtering of light samples. - private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 600; - private static final long DIMMING_LIGHT_TIME_CONSTANT = 4000; + // IIR filter time constants in milliseconds for computing two moving averages of + // the light samples. One is a long-term average and the other is a short-term average. + // We can use these filters to assess trends in ambient brightness. + // The short term average gives us a filtered but relatively low latency measurement. + // The long term average informs us about the overall trend. + private static final long SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 1000; + private static final long LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 8000; // Stability requirements in milliseconds for accepting a new brightness // level. This is used for debouncing the light sensor. Different constants - // are used to debounce the light sensor when adapting to brighter or dimmer - // environments. + // are used to debounce the light sensor when adapting to brighter or darker environments. // This parameter controls how quickly brightness changes occur in response to - // an observed change in light level. - private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500; - private static final long DIMMING_LIGHT_DEBOUNCE = 10000; + // an observed change in light level following a previous change in the opposite direction. + private static final long BRIGHTENING_LIGHT_DEBOUNCE = 5000; + 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(); @@ -284,39 +289,28 @@ final class DisplayPowerController { // The time when the light sensor was enabled. private long mLightSensorEnableTime; - // The currently accepted average light sensor value. - private float mLightMeasurement; - - // True if the light sensor measurement is valid. - private boolean mLightMeasurementValid; - - // The number of light sensor samples that have been collected since the - // last time a light sensor reading was accepted. - private int mRecentLightSamples; - - // The moving average of recent light sensor values. - private float mRecentLightAverage; + // The currently accepted nominal ambient light level. + private float mAmbientLux; - // True if recent light samples are getting brighter than the previous - // stable light measurement. - private boolean mRecentLightBrightening; + // True if mAmbientLux holds a valid value. + private boolean mAmbientLuxValid; - // The time constant to use for filtering based on whether the - // light appears to be brightening or dimming. - private long mRecentLightTimeConstant; + // The time when the ambient lux was last brightened or darkened. + private long mLastAmbientBrightenTime; + private long mLastAmbientDarkenTime; // The most recent light sample. - private float mLastLightSample; + private float mLastObservedLux; // 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. - private long mFirstRecentLightSampleTime; + // The number of light samples collected since the light sensor was enabled. + private int mRecentLightSamples; - // The upcoming debounce light sensor time. - // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1. - private long mPendingLightSensorDebounceTime; + // The long-term and short-term filtered light measurements. + private float mRecentShortTermAverageLux; + private float mRecentLongTermAverageLux; // The screen brightness level that has been chosen by the auto-brightness // algorithm. The actual brightness should ramp towards this value. @@ -873,7 +867,8 @@ final class DisplayPowerController { } else { if (mLightSensorEnabled) { mLightSensorEnabled = false; - mLightMeasurementValid = false; + mAmbientLuxValid = false; + mRecentLightSamples = 0; mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); mSensorManager.unregisterListener(mLightSensorListener); } @@ -884,114 +879,99 @@ final class DisplayPowerController { } private void handleLightSensorEvent(long time, float lux) { - // Take the first few readings during the warm-up period and apply them - // immediately without debouncing. - if (!mLightMeasurementValid - || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) { - mLightMeasurement = lux; - mLightMeasurementValid = true; - mRecentLightSamples = 0; - updateAutoBrightness(true); + // Update our filters. + mRecentLightSamples += 1; + if (mRecentLightSamples == 1) { + mRecentShortTermAverageLux = lux; + mRecentLongTermAverageLux = lux; + } 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); } - // 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)); - } + // Remember this sample value. + mLastObservedLux = lux; + mLastObservedLuxTime = time; - // Debounce. + // Update the ambient lux level. 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); + updateAmbientLux(time); } - private void accumulateRecentLight(long time, float lux) { - final long timeDelta = time - mLastLightSampleTime; - mRecentLightSamples += 1; - mRecentLightAverage += (lux - mRecentLightAverage) * - timeDelta / (mRecentLightTimeConstant + timeDelta); - mLastLightSample = lux; - mLastLightSampleTime = time; - } - - private void debounceLightSensor() { - if (mLightMeasurementValid && mRecentLightSamples >= 1) { - final long now = SystemClock.uptimeMillis(); - if (mPendingLightSensorDebounceTime <= now) { - accumulateRecentLight(now, mLastLightSample); - mLightMeasurement = mRecentLightAverage; + 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 + || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) { + 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) { - Slog.d(TAG, "debounceLightSensor: Accepted new measurement " - + mLightMeasurement + " after " - + (now - mFirstRecentLightSampleTime) + " ms based on " - + mRecentLightSamples + " recent samples."); + Slog.d(TAG, "updateAmbientLux: Brightened: " + + "mAmbientLux=" + mAmbientLux + + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux + + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); } - + mLastAmbientBrightenTime = time; + mAmbientLux = mRecentShortTermAverageLux; updateAutoBrightness(true); + } else { + mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime); + } + return; + } - // 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; + // 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 { - Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED); - msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime); + mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime); } } } + private void debounceLightSensor() { + updateAmbientLux(SystemClock.uptimeMillis()); + } + private void updateAutoBrightness(boolean sendUpdate) { - if (!mLightMeasurementValid) { + if (!mAmbientLuxValid) { return; } - float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement); + float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux); float gamma = 1.0f; if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT @@ -1031,7 +1011,7 @@ final class DisplayPowerController { } int newScreenAutoBrightness = clampScreenBrightness( - (int)Math.round(value * PowerManager.BRIGHTNESS_ON)); + Math.round(value * PowerManager.BRIGHTNESS_ON)); if (mScreenAutoBrightness != newScreenAutoBrightness) { if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" @@ -1152,19 +1132,18 @@ final class DisplayPowerController { pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime)); - pw.println(" mLightMeasurement=" + mLightMeasurement); - pw.println(" mLightMeasurementValid=" + mLightMeasurementValid); - pw.println(" mLastLightSample=" + mLastLightSample); - pw.println(" mLastLightSampleTime=" - + TimeUtils.formatUptime(mLastLightSampleTime)); + pw.println(" mAmbientLux=" + mAmbientLux); + pw.println(" mAmbientLuxValid=" + mAmbientLuxValid); + pw.println(" mLastAmbientBrightenTime=" + + TimeUtils.formatUptime(mLastAmbientBrightenTime)); + pw.println(" mLastAmbientDimTime=" + + TimeUtils.formatUptime(mLastAmbientDarkenTime)); + pw.println(" mLastObservedLux=" + mLastObservedLux); + pw.println(" mLastObservedLuxTime=" + + TimeUtils.formatUptime(mLastObservedLuxTime)); pw.println(" mRecentLightSamples=" + mRecentLightSamples); - pw.println(" mRecentLightAverage=" + mRecentLightAverage); - pw.println(" mRecentLightBrightening=" + mRecentLightBrightening); - pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant); - pw.println(" mFirstRecentLightSampleTime=" - + TimeUtils.formatUptime(mFirstRecentLightSampleTime)); - pw.println(" mPendingLightSensorDebounceTime=" - + TimeUtils.formatUptime(mPendingLightSensorDebounceTime)); + pw.println(" mRecentShortTermAverageLux=" + mRecentShortTermAverageLux); + pw.println(" mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness); pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java index c9b5d90..648c0c5 100644 --- a/services/java/com/android/server/power/PhotonicModulator.java +++ b/services/java/com/android/server/power/PhotonicModulator.java @@ -16,6 +16,8 @@ package com.android.server.power; +import android.util.Slog; + import com.android.server.LightsService; import java.util.concurrent.Executor; @@ -27,6 +29,9 @@ import java.util.concurrent.Executor; * setting the backlight brightness is especially slow. */ final class PhotonicModulator { + private static final String TAG = "PhotonicModulator"; + private static final boolean DEBUG = false; + private static final int UNKNOWN_LIGHT_VALUE = -1; private final Object mLock = new Object(); @@ -58,6 +63,9 @@ final class PhotonicModulator { synchronized (mLock) { if (lightValue != mPendingLightValue) { mPendingLightValue = lightValue; + if (DEBUG) { + Slog.d(TAG, "Enqueuing request to change brightness to " + lightValue); + } if (!mPendingChange) { mPendingChange = true; mSuspendBlocker.acquire(); @@ -91,6 +99,9 @@ final class PhotonicModulator { } mActualLightValue = newLightValue; } + if (DEBUG) { + Slog.d(TAG, "Setting brightness to " + newLightValue); + } mLight.setBrightness(newLightValue); } } |