2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.display;
19 import com.android.server.EventLogTags;
20 import com.android.server.LocalServices;
21 import com.android.server.twilight.TwilightListener;
22 import com.android.server.twilight.TwilightManager;
23 import com.android.server.twilight.TwilightState;
25 import android.hardware.Sensor;
26 import android.hardware.SensorEvent;
27 import android.hardware.SensorEventListener;
28 import android.hardware.SensorManager;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.PowerManager;
33 import android.os.SystemClock;
34 import android.text.format.DateUtils;
35 import android.util.EventLog;
36 import android.util.MathUtils;
37 import android.util.Spline;
38 import android.util.Slog;
39 import android.util.TimeUtils;
41 import java.io.PrintWriter;
43 class AutomaticBrightnessController {
44 private static final String TAG = "AutomaticBrightnessController";
46 private static final boolean DEBUG = false;
47 private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
49 // If true, enables the use of the screen auto-brightness adjustment setting.
50 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
52 // Hysteresis constraints for brightening or darkening.
53 // The recent lux must have changed by at least this fraction relative to the
54 // current ambient lux before a change will be considered.
55 private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
56 private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
58 // Threshold (in lux) to select between normal and fast debounce time.
59 // If the difference between last sample and weighted value is larger than this value,
60 // fast debounce is used.
61 private static final float BRIGHTENING_FAST_THRESHOLD = 1000f;
63 // How long the current sensor reading is assumed to be valid beyond the current time.
64 // This provides a bit of prediction, as well as ensures that the weight for the last sample is
65 // non-zero, which in turn ensures that the total weight is non-zero.
66 private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
68 // Specifies the maximum magnitude of the time of day adjustment.
69 private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f;
71 // Debounce for sampling user-initiated changes in display brightness to ensure
72 // the user is satisfied with the result before storing the sample.
73 private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
75 private static final int MSG_UPDATE_AMBIENT_LUX = 1;
76 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
78 // Callbacks for requesting updates to the the display's power state
79 private final Callbacks mCallbacks;
81 // The sensor manager.
82 private final SensorManager mSensorManager;
84 // The light sensor, or null if not available or needed.
85 private final Sensor mLightSensor;
87 // The twilight service.
88 private final TwilightManager mTwilight;
90 // The auto-brightness spline adjustment.
91 // The brightness values have been scaled to a range of 0..1.
92 private final Spline mScreenAutoBrightnessSpline;
94 // The minimum and maximum screen brightnesses.
95 private final int mScreenBrightnessRangeMinimum;
96 private final int mScreenBrightnessRangeMaximum;
97 private final float mDozeScaleFactor;
99 // Light sensor event rate in milliseconds.
100 private final int mLightSensorRate;
102 // Stability requirements in milliseconds for accepting a new brightness level. This is used
103 // for debouncing the light sensor. Different constants are used to debounce the light sensor
104 // when adapting to brighter or darker environments. This parameter controls how quickly
105 // brightness changes occur in response to an observed change in light level that exceeds the
106 // hysteresis threshold.
107 private final long mBrighteningLightFastDebounceConfig;
108 private final long mBrighteningLightDebounceConfig;
109 private final long mDarkeningLightDebounceConfig;
111 // If true immediately after the screen is turned on the controller will try to adjust the
112 // brightness based on the current sensor reads. If false, the controller will collect more data
113 // and only then decide whether to change brightness.
114 private final boolean mResetAmbientLuxAfterWarmUpConfig;
116 // Period of time in which to consider light samples in milliseconds.
117 private final int mAmbientLightHorizon;
119 // The intercept used for the weighting calculation. This is used in order to keep all possible
120 // weighting values positive.
121 private final int mWeightingIntercept;
123 // Amount of time to delay auto-brightness after screen on while waiting for
124 // the light sensor to warm-up in milliseconds.
125 // May be 0 if no warm-up is required.
126 private int mLightSensorWarmUpTimeConfig;
128 // Set to true if the light sensor is enabled.
129 private boolean mLightSensorEnabled;
131 // The time when the light sensor was enabled.
132 private long mLightSensorEnableTime;
134 // The currently accepted nominal ambient light level.
135 private float mAmbientLux;
137 // True if mAmbientLux holds a valid value.
138 private boolean mAmbientLuxValid;
140 // The ambient light level threshold at which to brighten or darken the screen.
141 private float mBrighteningLuxThreshold;
142 private float mDarkeningLuxThreshold;
144 // The most recent light sample.
145 private float mLastObservedLux;
147 // The time of the most light recent sample.
148 private long mLastObservedLuxTime;
150 // The number of light samples collected since the light sensor was enabled.
151 private int mRecentLightSamples;
153 // A ring buffer containing all of the recent ambient light sensor readings.
154 private AmbientLightRingBuffer mAmbientLightRingBuffer;
156 // A ring buffer containing the light sensor readings for the initial horizon period.
157 private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
160 private AutomaticBrightnessHandler mHandler;
162 // The screen brightness level that has been chosen by the auto-brightness
163 // algorithm. The actual brightness should ramp towards this value.
164 // We preserve this value even when we stop using the light sensor so
165 // that we can quickly revert to the previous auto-brightness level
166 // while the light sensor warms up.
167 // Use -1 if there is no current auto-brightness value available.
168 private int mScreenAutoBrightness = -1;
170 // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
171 private float mScreenAutoBrightnessAdjustment = 0.0f;
173 // The maximum range of gamma adjustment possible using the screen
174 // auto-brightness adjustment setting.
175 private float mScreenAutoBrightnessAdjustmentMaxGamma;
177 // The last screen auto-brightness gamma. (For printing in dump() only.)
178 private float mLastScreenAutoBrightnessGamma = 1.0f;
180 // Are we going to adjust brightness while dozing.
181 private boolean mDozing;
183 // True if we are collecting a brightness adjustment sample, along with some data
184 // for the initial state of the sample.
185 private boolean mBrightnessAdjustmentSamplePending;
186 private float mBrightnessAdjustmentSampleOldAdjustment;
187 private float mBrightnessAdjustmentSampleOldLux;
188 private int mBrightnessAdjustmentSampleOldBrightness;
189 private float mBrightnessAdjustmentSampleOldGamma;
191 private boolean mUseTwilight;
193 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
194 SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
195 int brightnessMin, int brightnessMax, float dozeScaleFactor,
196 int lightSensorRate, long brighteningLightDebounceConfig,
197 long brighteningLightFastDebounceConfig,
198 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
199 int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma ) {
200 mCallbacks = callbacks;
201 mTwilight = LocalServices.getService(TwilightManager.class);
202 mSensorManager = sensorManager;
203 mScreenAutoBrightnessSpline = autoBrightnessSpline;
204 mScreenBrightnessRangeMinimum = brightnessMin;
205 mScreenBrightnessRangeMaximum = brightnessMax;
206 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
207 mDozeScaleFactor = dozeScaleFactor;
208 mLightSensorRate = lightSensorRate;
209 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
210 mBrighteningLightFastDebounceConfig = brighteningLightFastDebounceConfig;
211 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
212 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
213 mAmbientLightHorizon = ambientLightHorizon;
214 mWeightingIntercept = ambientLightHorizon;
215 mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
217 mHandler = new AutomaticBrightnessHandler(looper);
218 mAmbientLightRingBuffer =
219 new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
220 mInitialHorizonAmbientLightRingBuffer =
221 new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
223 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
224 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
228 public int getAutomaticScreenBrightness() {
230 return (int) (mScreenAutoBrightness * mDozeScaleFactor);
232 return mScreenAutoBrightness;
235 public void configure(boolean enable, float adjustment, boolean dozing,
236 boolean userInitiatedChange, boolean useTwilight) {
237 // While dozing, the application processor may be suspended which will prevent us from
238 // receiving new information from the light sensor. On some devices, we may be able to
239 // switch to a wake-up light sensor instead but for now we will simply disable the sensor
240 // and hold onto the last computed screen auto brightness. We save the dozing flag for
241 // debugging purposes.
243 boolean changed = setLightSensorEnabled(enable && !dozing);
244 changed |= setScreenAutoBrightnessAdjustment(adjustment);
245 changed |= setUseTwilight(useTwilight);
247 updateAutoBrightness(false /*sendUpdate*/);
249 if (enable && !dozing && userInitiatedChange) {
250 prepareBrightnessAdjustmentSample();
254 private boolean setUseTwilight(boolean useTwilight) {
255 if (mUseTwilight == useTwilight) return false;
257 mTwilight.registerListener(mTwilightListener, mHandler);
259 mTwilight.unregisterListener(mTwilightListener);
261 mUseTwilight = useTwilight;
265 public void dump(PrintWriter pw) {
267 pw.println("Automatic Brightness Controller Configuration:");
268 pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
269 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
270 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
271 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
272 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
273 pw.println(" mBrighteningLightFastDebounceConfig=" + mBrighteningLightFastDebounceConfig);
274 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
275 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
278 pw.println("Automatic Brightness Controller State:");
279 pw.println(" mLightSensor=" + mLightSensor);
280 pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
281 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
282 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
283 pw.println(" mAmbientLux=" + mAmbientLux);
284 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
285 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
286 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
287 pw.println(" mLastObservedLux=" + mLastObservedLux);
288 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
289 pw.println(" mRecentLightSamples=" + mRecentLightSamples);
290 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
291 pw.println(" mInitialHorizonAmbientLightRingBuffer=" +
292 mInitialHorizonAmbientLightRingBuffer);
293 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
294 pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
295 pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
296 pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
297 pw.println(" mDozing=" + mDozing);
300 private boolean setLightSensorEnabled(boolean enable) {
302 if (!mLightSensorEnabled) {
303 mLightSensorEnabled = true;
304 mLightSensorEnableTime = SystemClock.uptimeMillis();
305 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
306 mLightSensorRate * 1000, mHandler);
310 if (mLightSensorEnabled) {
311 mLightSensorEnabled = false;
312 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
313 mRecentLightSamples = 0;
314 mAmbientLightRingBuffer.clear();
315 mInitialHorizonAmbientLightRingBuffer.clear();
316 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
317 mSensorManager.unregisterListener(mLightSensorListener);
323 private void handleLightSensorEvent(long time, float lux) {
324 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
326 if (DEBUG) Slog.d(TAG, "handleLightSensorEvent: time=" + time + ", lux=" + lux);
327 applyLightSensorMeasurement(time, lux);
328 updateAmbientLux(time);
331 private void applyLightSensorMeasurement(long time, float lux) {
332 mRecentLightSamples++;
333 // Store all of the light measurements for the intial horizon period. This is to help
334 // diagnose dim wake ups and slow responses in b/27951906.
335 if (time <= mLightSensorEnableTime + mAmbientLightHorizon) {
336 mInitialHorizonAmbientLightRingBuffer.push(time, lux);
338 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
339 mAmbientLightRingBuffer.push(time, lux);
341 // Remember this sample value.
342 mLastObservedLux = lux;
343 mLastObservedLuxTime = time;
346 private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
347 if (adjustment != mScreenAutoBrightnessAdjustment) {
348 mScreenAutoBrightnessAdjustment = adjustment;
354 private void setAmbientLux(float lux) {
356 mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
357 mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
360 private float calculateAmbientLux(long now) {
361 final int N = mAmbientLightRingBuffer.size();
363 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
367 float totalWeight = 0;
368 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
369 for (int i = N - 1; i >= 0; i--) {
370 long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
371 float weight = calculateWeight(startTime, endTime);
372 float lux = mAmbientLightRingBuffer.getLux(i);
374 Slog.d(TAG, "calculateAmbientLux: [" +
376 (endTime) + "]: lux=" + lux + ", weight=" + weight);
378 totalWeight += weight;
379 sum += mAmbientLightRingBuffer.getLux(i) * weight;
383 Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
384 ", newAmbientLux=" + (sum / totalWeight));
386 return sum / totalWeight;
389 private float calculateWeight(long startDelta, long endDelta) {
390 return weightIntegral(endDelta) - weightIntegral(startDelta);
393 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
394 // horizon we're looking at and provides a non-linear weighting for light samples.
395 private float weightIntegral(long x) {
396 return x * (x * 0.5f + mWeightingIntercept);
399 private long nextAmbientLightBrighteningTransition(long time, float ambientLux) {
400 final int N = mAmbientLightRingBuffer.size();
401 long earliestValidTime = time;
402 for (int i = N - 1; i >= 0; i--) {
403 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
406 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
409 long debounceDelay = mLastObservedLux - ambientLux > BRIGHTENING_FAST_THRESHOLD
410 ? mBrighteningLightFastDebounceConfig : mBrighteningLightDebounceConfig;
411 return earliestValidTime + debounceDelay;
414 private long nextAmbientLightDarkeningTransition(long time, float ambientLux) {
415 final int N = mAmbientLightRingBuffer.size();
416 long earliestValidTime = time;
417 for (int i = N - 1; i >= 0; i--) {
418 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
421 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
423 return earliestValidTime + mDarkeningLightDebounceConfig;
426 private void updateAmbientLux() {
427 long time = SystemClock.uptimeMillis();
428 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
429 updateAmbientLux(time);
432 private void updateAmbientLux(long time) {
433 // If the light sensor was just turned on then immediately update our initial
434 // estimate of the current ambient light level.
435 if (!mAmbientLuxValid) {
436 final long timeWhenSensorWarmedUp =
437 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
438 if (time < timeWhenSensorWarmedUp) {
440 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
442 + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
444 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
445 timeWhenSensorWarmedUp);
448 setAmbientLux(calculateAmbientLux(time));
449 mAmbientLuxValid = true;
451 Slog.d(TAG, "updateAmbientLux: Initializing: "
452 + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
453 + ", mAmbientLux=" + mAmbientLux);
455 updateAutoBrightness(true);
458 float ambientLux = calculateAmbientLux(time);
459 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
460 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
462 if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
463 || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
465 Slog.d(TAG, "updateAmbientLux: "
466 + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
467 + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
468 + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
469 + ", mAmbientLux=" + mAmbientLux);
471 setAmbientLux(ambientLux);
472 updateAutoBrightness(true);
473 nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
474 nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
476 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
477 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
478 // exceed the necessary threshold, then it's possible we'll get a transition time prior to
479 // now. Rather than continually checking to see whether the weighted lux exceeds the
480 // threshold, schedule an update for when we'd normally expect another light sample, which
481 // should be enough time to decide whether we should actually transition to the new
482 // weighted ambient lux or not.
484 nextTransitionTime > time ? nextTransitionTime : time + mLightSensorRate;
486 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
487 + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
489 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
492 private void updateAutoBrightness(boolean sendUpdate) {
493 if (!mAmbientLuxValid) {
497 float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
500 if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
501 && mScreenAutoBrightnessAdjustment != 0.0f) {
502 final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma,
503 Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
506 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
511 TwilightState state = mTwilight.getCurrentState();
512 if (state != null && state.isNight()) {
513 final long now = System.currentTimeMillis();
514 gamma *= 1 + state.getAmount() * TWILIGHT_ADJUSTMENT_MAX_GAMMA;
516 Slog.d(TAG, "updateAutoBrightness: twilight amount=" + state.getAmount());
522 final float in = value;
523 value = MathUtils.pow(value, gamma);
525 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
526 + ", in=" + in + ", out=" + value);
530 int newScreenAutoBrightness =
531 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
532 if (mScreenAutoBrightness != newScreenAutoBrightness) {
534 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
535 + mScreenAutoBrightness + ", newScreenAutoBrightness="
536 + newScreenAutoBrightness);
539 mScreenAutoBrightness = newScreenAutoBrightness;
540 mLastScreenAutoBrightnessGamma = gamma;
542 mCallbacks.updateBrightness();
547 private int clampScreenBrightness(int value) {
548 return MathUtils.constrain(value,
549 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
552 private void prepareBrightnessAdjustmentSample() {
553 if (!mBrightnessAdjustmentSamplePending) {
554 mBrightnessAdjustmentSamplePending = true;
555 mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment;
556 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
557 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
558 mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma;
560 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
563 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
564 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
567 private void cancelBrightnessAdjustmentSample() {
568 if (mBrightnessAdjustmentSamplePending) {
569 mBrightnessAdjustmentSamplePending = false;
570 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
574 private void collectBrightnessAdjustmentSample() {
575 if (mBrightnessAdjustmentSamplePending) {
576 mBrightnessAdjustmentSamplePending = false;
577 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
579 Slog.d(TAG, "Auto-brightness adjustment changed by user: "
580 + "adj=" + mScreenAutoBrightnessAdjustment
581 + ", lux=" + mAmbientLux
582 + ", brightness=" + mScreenAutoBrightness
583 + ", gamma=" + mLastScreenAutoBrightnessGamma
584 + ", ring=" + mAmbientLightRingBuffer);
587 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
588 mBrightnessAdjustmentSampleOldAdjustment,
589 mBrightnessAdjustmentSampleOldLux,
590 mBrightnessAdjustmentSampleOldBrightness,
591 mBrightnessAdjustmentSampleOldGamma,
592 mScreenAutoBrightnessAdjustment,
594 mScreenAutoBrightness,
595 mLastScreenAutoBrightnessGamma);
600 private final class AutomaticBrightnessHandler extends Handler {
601 public AutomaticBrightnessHandler(Looper looper) {
602 super(looper, null, true /*async*/);
606 public void handleMessage(Message msg) {
608 case MSG_UPDATE_AMBIENT_LUX:
612 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
613 collectBrightnessAdjustmentSample();
619 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
621 public void onSensorChanged(SensorEvent event) {
622 if (mLightSensorEnabled) {
623 final long time = SystemClock.uptimeMillis();
624 final float lux = event.values[0];
625 handleLightSensorEvent(time, lux);
630 public void onAccuracyChanged(Sensor sensor, int accuracy) {
635 private final TwilightListener mTwilightListener = new TwilightListener() {
637 public void onTwilightStateChanged() {
638 updateAutoBrightness(true /*sendUpdate*/);
642 /** Callbacks to request updates to the display's power state. */
643 interface Callbacks {
644 void updateBrightness();
647 private static final class AmbientLightRingBuffer {
648 // Proportional extra capacity of the buffer beyond the expected number of light samples
650 private static final float BUFFER_SLACK = 1.5f;
651 private float[] mRingLux;
652 private long[] mRingTime;
653 private int mCapacity;
655 // The first valid element and the next open slot.
656 // Note that if mCount is zero then there are no valid elements.
661 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
662 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
663 mRingLux = new float[mCapacity];
664 mRingTime = new long[mCapacity];
667 public float getLux(int index) {
668 return mRingLux[offsetOf(index)];
671 public long getTime(int index) {
672 return mRingTime[offsetOf(index)];
675 public void push(long time, float lux) {
677 if (mCount == mCapacity) {
678 int newSize = mCapacity * 2;
680 float[] newRingLux = new float[newSize];
681 long[] newRingTime = new long[newSize];
682 int length = mCapacity - mStart;
683 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
684 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
686 System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
687 System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
689 mRingLux = newRingLux;
690 mRingTime = newRingTime;
696 mRingTime[next] = time;
697 mRingLux[next] = lux;
699 if (mEnd == mCapacity) {
705 public void prune(long horizon) {
711 int next = mStart + 1;
712 if (next >= mCapacity) {
715 if (mRingTime[next] > horizon) {
716 // Some light sensors only produce data upon a change in the ambient light
717 // levels, so we need to consider the previous measurement as the ambient light
718 // level for all points in time up until we receive a new measurement. Thus, we
719 // always want to keep the youngest element that would be removed from the
720 // buffer and just set its measurement time to the horizon time since at that
721 // point it is the ambient light level, and to remove it would be to drop a
722 // valid data point within our horizon.
729 if (mRingTime[mStart] < horizon) {
730 mRingTime[mStart] = horizon;
738 public void clear() {
745 public String toString() {
746 StringBuffer buf = new StringBuffer();
748 for (int i = 0; i < mCount; i++) {
749 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
753 buf.append(getLux(i));
755 buf.append(next - getTime(i));
759 return buf.toString();
762 private int offsetOf(int index) {
763 if (index >= mCount || index < 0) {
764 throw new ArrayIndexOutOfBoundsException(index);
767 if (index >= mCapacity) {