OSDN Git Service

c16dac23d4c7a0522511e9915ad9d843c6e4b5dd
[android-x86/frameworks-base.git] / services / core / java / com / android / server / display / AutomaticBrightnessController.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.server.display;
18
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;
24
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;
40
41 import java.io.PrintWriter;
42
43 class AutomaticBrightnessController {
44     private static final String TAG = "AutomaticBrightnessController";
45
46     private static final boolean DEBUG = false;
47     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
48
49     // If true, enables the use of the screen auto-brightness adjustment setting.
50     private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
51
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;
57
58     // How long the current sensor reading is assumed to be valid beyond the current time.
59     // This provides a bit of prediction, as well as ensures that the weight for the last sample is
60     // non-zero, which in turn ensures that the total weight is non-zero.
61     private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
62
63     // Specifies the maximum magnitude of the time of day adjustment.
64     private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f;
65
66     // Debounce for sampling user-initiated changes in display brightness to ensure
67     // the user is satisfied with the result before storing the sample.
68     private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
69
70     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
71     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
72
73     // Callbacks for requesting updates to the the display's power state
74     private final Callbacks mCallbacks;
75
76     // The sensor manager.
77     private final SensorManager mSensorManager;
78
79     // The light sensor, or null if not available or needed.
80     private final Sensor mLightSensor;
81
82     // The twilight service.
83     private final TwilightManager mTwilight;
84
85     // The auto-brightness spline adjustment.
86     // The brightness values have been scaled to a range of 0..1.
87     private final Spline mScreenAutoBrightnessSpline;
88
89     // The minimum and maximum screen brightnesses.
90     private final int mScreenBrightnessRangeMinimum;
91     private final int mScreenBrightnessRangeMaximum;
92     private final float mDozeScaleFactor;
93
94     // Light sensor event rate in milliseconds.
95     private final int mLightSensorRate;
96
97     // Stability requirements in milliseconds for accepting a new brightness level.  This is used
98     // for debouncing the light sensor.  Different constants are used to debounce the light sensor
99     // when adapting to brighter or darker environments.  This parameter controls how quickly
100     // brightness changes occur in response to an observed change in light level that exceeds the
101     // hysteresis threshold.
102     private final long mBrighteningLightDebounceConfig;
103     private final long mDarkeningLightDebounceConfig;
104
105     // If true immediately after the screen is turned on the controller will try to adjust the
106     // brightness based on the current sensor reads. If false, the controller will collect more data
107     // and only then decide whether to change brightness.
108     private final boolean mResetAmbientLuxAfterWarmUpConfig;
109
110     // Period of time in which to consider light samples in milliseconds.
111     private final int mAmbientLightHorizon;
112
113     // The intercept used for the weighting calculation. This is used in order to keep all possible
114     // weighting values positive.
115     private final int mWeightingIntercept;
116
117     // Amount of time to delay auto-brightness after screen on while waiting for
118     // the light sensor to warm-up in milliseconds.
119     // May be 0 if no warm-up is required.
120     private int mLightSensorWarmUpTimeConfig;
121
122     // Set to true if the light sensor is enabled.
123     private boolean mLightSensorEnabled;
124
125     // The time when the light sensor was enabled.
126     private long mLightSensorEnableTime;
127
128     // The currently accepted nominal ambient light level.
129     private float mAmbientLux;
130
131     // True if mAmbientLux holds a valid value.
132     private boolean mAmbientLuxValid;
133
134     // The ambient light level threshold at which to brighten or darken the screen.
135     private float mBrighteningLuxThreshold;
136     private float mDarkeningLuxThreshold;
137
138     // The most recent light sample.
139     private float mLastObservedLux;
140
141     // The time of the most light recent sample.
142     private long mLastObservedLuxTime;
143
144     // The number of light samples collected since the light sensor was enabled.
145     private int mRecentLightSamples;
146
147     // A ring buffer containing all of the recent ambient light sensor readings.
148     private AmbientLightRingBuffer mAmbientLightRingBuffer;
149
150     // A ring buffer containing the light sensor readings for the initial horizon period.
151     private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
152
153     // The handler
154     private AutomaticBrightnessHandler mHandler;
155
156     // The screen brightness level that has been chosen by the auto-brightness
157     // algorithm.  The actual brightness should ramp towards this value.
158     // We preserve this value even when we stop using the light sensor so
159     // that we can quickly revert to the previous auto-brightness level
160     // while the light sensor warms up.
161     // Use -1 if there is no current auto-brightness value available.
162     private int mScreenAutoBrightness = -1;
163
164     // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
165     private float mScreenAutoBrightnessAdjustment = 0.0f;
166
167     // The maximum range of gamma adjustment possible using the screen
168     // auto-brightness adjustment setting.
169     private float mScreenAutoBrightnessAdjustmentMaxGamma;
170
171     // The last screen auto-brightness gamma.  (For printing in dump() only.)
172     private float mLastScreenAutoBrightnessGamma = 1.0f;
173
174     // Are we going to adjust brightness while dozing.
175     private boolean mDozing;
176
177     // True if we are collecting a brightness adjustment sample, along with some data
178     // for the initial state of the sample.
179     private boolean mBrightnessAdjustmentSamplePending;
180     private float mBrightnessAdjustmentSampleOldAdjustment;
181     private float mBrightnessAdjustmentSampleOldLux;
182     private int mBrightnessAdjustmentSampleOldBrightness;
183     private float mBrightnessAdjustmentSampleOldGamma;
184
185     private boolean mUseTwilight;
186
187     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
188             SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
189             int brightnessMin, int brightnessMax, float dozeScaleFactor,
190             int lightSensorRate, long brighteningLightDebounceConfig,
191             long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
192             int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma ) {
193         mCallbacks = callbacks;
194         mTwilight = LocalServices.getService(TwilightManager.class);
195         mSensorManager = sensorManager;
196         mScreenAutoBrightnessSpline = autoBrightnessSpline;
197         mScreenBrightnessRangeMinimum = brightnessMin;
198         mScreenBrightnessRangeMaximum = brightnessMax;
199         mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
200         mDozeScaleFactor = dozeScaleFactor;
201         mLightSensorRate = lightSensorRate;
202         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
203         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
204         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
205         mAmbientLightHorizon = ambientLightHorizon;
206         mWeightingIntercept = ambientLightHorizon;
207         mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
208
209         mHandler = new AutomaticBrightnessHandler(looper);
210         mAmbientLightRingBuffer =
211             new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
212         mInitialHorizonAmbientLightRingBuffer =
213             new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
214
215         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
216             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
217         }
218     }
219
220     public int getAutomaticScreenBrightness() {
221         if (mDozing) {
222             return (int) (mScreenAutoBrightness * mDozeScaleFactor);
223         }
224         return mScreenAutoBrightness;
225     }
226
227     public void configure(boolean enable, float adjustment, boolean dozing,
228             boolean userInitiatedChange, boolean useTwilight) {
229         // While dozing, the application processor may be suspended which will prevent us from
230         // receiving new information from the light sensor. On some devices, we may be able to
231         // switch to a wake-up light sensor instead but for now we will simply disable the sensor
232         // and hold onto the last computed screen auto brightness.  We save the dozing flag for
233         // debugging purposes.
234         mDozing = dozing;
235         boolean changed = setLightSensorEnabled(enable && !dozing);
236         changed |= setScreenAutoBrightnessAdjustment(adjustment);
237         changed |= setUseTwilight(useTwilight);
238         if (changed) {
239             updateAutoBrightness(false /*sendUpdate*/);
240         }
241         if (enable && !dozing && userInitiatedChange) {
242             prepareBrightnessAdjustmentSample();
243         }
244     }
245
246     private boolean setUseTwilight(boolean useTwilight) {
247         if (mUseTwilight == useTwilight) return false;
248         if (useTwilight) {
249             mTwilight.registerListener(mTwilightListener, mHandler);
250         } else {
251             mTwilight.unregisterListener(mTwilightListener);
252         }
253         mUseTwilight = useTwilight;
254         return true;
255     }
256
257     public void dump(PrintWriter pw) {
258         pw.println();
259         pw.println("Automatic Brightness Controller Configuration:");
260         pw.println("  mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
261         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
262         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
263         pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
264         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
265         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
266         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
267
268         pw.println();
269         pw.println("Automatic Brightness Controller State:");
270         pw.println("  mLightSensor=" + mLightSensor);
271         pw.println("  mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
272         pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
273         pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
274         pw.println("  mAmbientLux=" + mAmbientLux);
275         pw.println("  mAmbientLightHorizon=" + mAmbientLightHorizon);
276         pw.println("  mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
277         pw.println("  mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
278         pw.println("  mLastObservedLux=" + mLastObservedLux);
279         pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
280         pw.println("  mRecentLightSamples=" + mRecentLightSamples);
281         pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
282         pw.println("  mInitialHorizonAmbientLightRingBuffer=" +
283                 mInitialHorizonAmbientLightRingBuffer);
284         pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
285         pw.println("  mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
286         pw.println("  mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
287         pw.println("  mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
288         pw.println("  mDozing=" + mDozing);
289     }
290
291     private boolean setLightSensorEnabled(boolean enable) {
292         if (enable) {
293             if (!mLightSensorEnabled) {
294                 mLightSensorEnabled = true;
295                 mLightSensorEnableTime = SystemClock.uptimeMillis();
296                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
297                         mLightSensorRate * 1000, mHandler);
298                 return true;
299             }
300         } else {
301             if (mLightSensorEnabled) {
302                 mLightSensorEnabled = false;
303                 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
304                 mRecentLightSamples = 0;
305                 mAmbientLightRingBuffer.clear();
306                 mInitialHorizonAmbientLightRingBuffer.clear();
307                 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
308                 mSensorManager.unregisterListener(mLightSensorListener);
309             }
310         }
311         return false;
312     }
313
314     private void handleLightSensorEvent(long time, float lux) {
315         mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
316
317         applyLightSensorMeasurement(time, lux);
318         updateAmbientLux(time);
319     }
320
321     private void applyLightSensorMeasurement(long time, float lux) {
322         mRecentLightSamples++;
323         // Store all of the light measurements for the intial horizon period. This is to help
324         // diagnose dim wake ups and slow responses in b/27951906.
325         if (time <= mLightSensorEnableTime + mAmbientLightHorizon) {
326             mInitialHorizonAmbientLightRingBuffer.push(time, lux);
327         }
328         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
329         mAmbientLightRingBuffer.push(time, lux);
330
331         // Remember this sample value.
332         mLastObservedLux = lux;
333         mLastObservedLuxTime = time;
334     }
335
336     private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
337         if (adjustment != mScreenAutoBrightnessAdjustment) {
338             mScreenAutoBrightnessAdjustment = adjustment;
339             return true;
340         }
341         return false;
342     }
343
344     private void setAmbientLux(float lux) {
345         mAmbientLux = lux;
346         mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
347         mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
348     }
349
350     private float calculateAmbientLux(long now) {
351         final int N = mAmbientLightRingBuffer.size();
352         if (N == 0) {
353             Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
354             return -1;
355         }
356         float sum = 0;
357         float totalWeight = 0;
358         long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
359         for (int i = N - 1; i >= 0; i--) {
360             long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
361             float weight = calculateWeight(startTime, endTime);
362             float lux = mAmbientLightRingBuffer.getLux(i);
363             if (DEBUG) {
364                 Slog.d(TAG, "calculateAmbientLux: [" +
365                         (startTime) + ", " +
366                         (endTime) + "]: lux=" + lux + ", weight=" + weight);
367             }
368             totalWeight += weight;
369             sum += mAmbientLightRingBuffer.getLux(i) * weight;
370             endTime = startTime;
371         }
372         if (DEBUG) {
373             Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
374                     ", newAmbientLux=" + (sum / totalWeight));
375         }
376         return sum / totalWeight;
377     }
378
379     private float calculateWeight(long startDelta, long endDelta) {
380         return weightIntegral(endDelta) - weightIntegral(startDelta);
381     }
382
383     // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
384     // horizon we're looking at and provides a non-linear weighting for light samples.
385     private float weightIntegral(long x) {
386         return x * (x * 0.5f + mWeightingIntercept);
387     }
388
389     private long nextAmbientLightBrighteningTransition(long time) {
390         final int N = mAmbientLightRingBuffer.size();
391         long earliestValidTime = time;
392         for (int i = N - 1; i >= 0; i--) {
393             if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
394                 break;
395             }
396             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
397         }
398         return earliestValidTime + mBrighteningLightDebounceConfig;
399     }
400
401     private long nextAmbientLightDarkeningTransition(long time) {
402         final int N = mAmbientLightRingBuffer.size();
403         long earliestValidTime = time;
404         for (int i = N - 1; i >= 0; i--) {
405             if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
406                 break;
407             }
408             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
409         }
410         return earliestValidTime + mDarkeningLightDebounceConfig;
411     }
412
413     private void updateAmbientLux() {
414         long time = SystemClock.uptimeMillis();
415         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
416         updateAmbientLux(time);
417     }
418
419     private void updateAmbientLux(long time) {
420         // If the light sensor was just turned on then immediately update our initial
421         // estimate of the current ambient light level.
422         if (!mAmbientLuxValid) {
423             final long timeWhenSensorWarmedUp =
424                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
425             if (time < timeWhenSensorWarmedUp) {
426                 if (DEBUG) {
427                     Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: "
428                             + "time=" + time
429                             + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
430                 }
431                 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
432                         timeWhenSensorWarmedUp);
433                 return;
434             }
435             setAmbientLux(calculateAmbientLux(time));
436             mAmbientLuxValid = true;
437             if (DEBUG) {
438                 Slog.d(TAG, "updateAmbientLux: Initializing: "
439                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
440                         + ", mAmbientLux=" + mAmbientLux);
441             }
442             updateAutoBrightness(true);
443         }
444
445         long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
446         long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
447         float ambientLux = calculateAmbientLux(time);
448
449         if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
450                 || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
451             setAmbientLux(ambientLux);
452             if (DEBUG) {
453                 Slog.d(TAG, "updateAmbientLux: "
454                         + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
455                         + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
456                         + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
457                         + ", mAmbientLux=" + mAmbientLux);
458             }
459             updateAutoBrightness(true);
460             nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
461             nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
462         }
463         long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
464         // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
465         // exceed the necessary threshold, then it's possible we'll get a transition time prior to
466         // now. Rather than continually checking to see whether the weighted lux exceeds the
467         // threshold, schedule an update for when we'd normally expect another light sample, which
468         // should be enough time to decide whether we should actually transition to the new
469         // weighted ambient lux or not.
470         nextTransitionTime =
471                 nextTransitionTime > time ? nextTransitionTime : time + mLightSensorRate;
472         if (DEBUG) {
473             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
474                     + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
475         }
476         mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
477     }
478
479     private void updateAutoBrightness(boolean sendUpdate) {
480         if (!mAmbientLuxValid) {
481             return;
482         }
483
484         float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
485         float gamma = 1.0f;
486
487         if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
488                 && mScreenAutoBrightnessAdjustment != 0.0f) {
489             final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma,
490                     Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
491             gamma *= adjGamma;
492             if (DEBUG) {
493                 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
494             }
495         }
496
497         if (mUseTwilight) {
498             TwilightState state = mTwilight.getCurrentState();
499             if (state != null && state.isNight()) {
500                 final long now = System.currentTimeMillis();
501                 gamma *= 1 + state.getAmount() * TWILIGHT_ADJUSTMENT_MAX_GAMMA;
502                 if (DEBUG) {
503                     Slog.d(TAG, "updateAutoBrightness: twilight amount=" + state.getAmount());
504                 }
505             }
506         }
507
508         if (gamma != 1.0f) {
509             final float in = value;
510             value = MathUtils.pow(value, gamma);
511             if (DEBUG) {
512                 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
513                         + ", in=" + in + ", out=" + value);
514             }
515         }
516
517         int newScreenAutoBrightness =
518                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
519         if (mScreenAutoBrightness != newScreenAutoBrightness) {
520             if (DEBUG) {
521                 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
522                         + mScreenAutoBrightness + ", newScreenAutoBrightness="
523                         + newScreenAutoBrightness);
524             }
525
526             mScreenAutoBrightness = newScreenAutoBrightness;
527             mLastScreenAutoBrightnessGamma = gamma;
528             if (sendUpdate) {
529                 mCallbacks.updateBrightness();
530             }
531         }
532     }
533
534     private int clampScreenBrightness(int value) {
535         return MathUtils.constrain(value,
536                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
537     }
538
539     private void prepareBrightnessAdjustmentSample() {
540         if (!mBrightnessAdjustmentSamplePending) {
541             mBrightnessAdjustmentSamplePending = true;
542             mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment;
543             mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
544             mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
545             mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma;
546         } else {
547             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
548         }
549
550         mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
551                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
552     }
553
554     private void cancelBrightnessAdjustmentSample() {
555         if (mBrightnessAdjustmentSamplePending) {
556             mBrightnessAdjustmentSamplePending = false;
557             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
558         }
559     }
560
561     private void collectBrightnessAdjustmentSample() {
562         if (mBrightnessAdjustmentSamplePending) {
563             mBrightnessAdjustmentSamplePending = false;
564             if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
565                 if (DEBUG) {
566                     Slog.d(TAG, "Auto-brightness adjustment changed by user: "
567                             + "adj=" + mScreenAutoBrightnessAdjustment
568                             + ", lux=" + mAmbientLux
569                             + ", brightness=" + mScreenAutoBrightness
570                             + ", gamma=" + mLastScreenAutoBrightnessGamma
571                             + ", ring=" + mAmbientLightRingBuffer);
572                 }
573
574                 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
575                         mBrightnessAdjustmentSampleOldAdjustment,
576                         mBrightnessAdjustmentSampleOldLux,
577                         mBrightnessAdjustmentSampleOldBrightness,
578                         mBrightnessAdjustmentSampleOldGamma,
579                         mScreenAutoBrightnessAdjustment,
580                         mAmbientLux,
581                         mScreenAutoBrightness,
582                         mLastScreenAutoBrightnessGamma);
583             }
584         }
585     }
586
587     private final class AutomaticBrightnessHandler extends Handler {
588         public AutomaticBrightnessHandler(Looper looper) {
589             super(looper, null, true /*async*/);
590         }
591
592         @Override
593         public void handleMessage(Message msg) {
594             switch (msg.what) {
595                 case MSG_UPDATE_AMBIENT_LUX:
596                     updateAmbientLux();
597                     break;
598
599                 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
600                     collectBrightnessAdjustmentSample();
601                     break;
602             }
603         }
604     }
605
606     private final SensorEventListener mLightSensorListener = new SensorEventListener() {
607         @Override
608         public void onSensorChanged(SensorEvent event) {
609             if (mLightSensorEnabled) {
610                 final long time = SystemClock.uptimeMillis();
611                 final float lux = event.values[0];
612                 handleLightSensorEvent(time, lux);
613             }
614         }
615
616         @Override
617         public void onAccuracyChanged(Sensor sensor, int accuracy) {
618             // Not used.
619         }
620     };
621
622     private final TwilightListener mTwilightListener = new TwilightListener() {
623         @Override
624         public void onTwilightStateChanged() {
625             updateAutoBrightness(true /*sendUpdate*/);
626         }
627     };
628
629     /** Callbacks to request updates to the display's power state. */
630     interface Callbacks {
631         void updateBrightness();
632     }
633
634     private static final class AmbientLightRingBuffer {
635         // Proportional extra capacity of the buffer beyond the expected number of light samples
636         // in the horizon
637         private static final float BUFFER_SLACK = 1.5f;
638         private float[] mRingLux;
639         private long[] mRingTime;
640         private int mCapacity;
641
642         // The first valid element and the next open slot.
643         // Note that if mCount is zero then there are no valid elements.
644         private int mStart;
645         private int mEnd;
646         private int mCount;
647
648         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
649             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
650             mRingLux = new float[mCapacity];
651             mRingTime = new long[mCapacity];
652         }
653
654         public float getLux(int index) {
655             return mRingLux[offsetOf(index)];
656         }
657
658         public long getTime(int index) {
659             return mRingTime[offsetOf(index)];
660         }
661
662         public void push(long time, float lux) {
663             int next = mEnd;
664             if (mCount == mCapacity) {
665                 int newSize = mCapacity * 2;
666
667                 float[] newRingLux = new float[newSize];
668                 long[] newRingTime = new long[newSize];
669                 int length = mCapacity - mStart;
670                 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
671                 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
672                 if (mStart != 0) {
673                     System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
674                     System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
675                 }
676                 mRingLux = newRingLux;
677                 mRingTime = newRingTime;
678
679                 next = mCapacity;
680                 mCapacity = newSize;
681                 mStart = 0;
682             }
683             mRingTime[next] = time;
684             mRingLux[next] = lux;
685             mEnd = next + 1;
686             if (mEnd == mCapacity) {
687                 mEnd = 0;
688             }
689             mCount++;
690         }
691
692         public void prune(long horizon) {
693             if (mCount == 0) {
694                 return;
695             }
696
697             while (mCount > 1) {
698                 int next = mStart + 1;
699                 if (next >= mCapacity) {
700                     next -= mCapacity;
701                 }
702                 if (mRingTime[next] > horizon) {
703                     // Some light sensors only produce data upon a change in the ambient light
704                     // levels, so we need to consider the previous measurement as the ambient light
705                     // level for all points in time up until we receive a new measurement. Thus, we
706                     // always want to keep the youngest element that would be removed from the
707                     // buffer and just set its measurement time to the horizon time since at that
708                     // point it is the ambient light level, and to remove it would be to drop a
709                     // valid data point within our horizon.
710                     break;
711                 }
712                 mStart = next;
713                 mCount -= 1;
714             }
715
716             if (mRingTime[mStart] < horizon) {
717                 mRingTime[mStart] = horizon;
718             }
719         }
720
721         public int size() {
722             return mCount;
723         }
724
725         public void clear() {
726             mStart = 0;
727             mEnd = 0;
728             mCount = 0;
729         }
730
731         @Override
732         public String toString() {
733             StringBuffer buf = new StringBuffer();
734             buf.append('[');
735             for (int i = 0; i < mCount; i++) {
736                 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
737                 if (i != 0) {
738                     buf.append(", ");
739                 }
740                 buf.append(getLux(i));
741                 buf.append(" / ");
742                 buf.append(next - getTime(i));
743                 buf.append("ms");
744             }
745             buf.append(']');
746             return buf.toString();
747         }
748
749         private int offsetOf(int index) {
750             if (index >= mCount || index < 0) {
751                 throw new ArrayIndexOutOfBoundsException(index);
752             }
753             index += mStart;
754             if (index >= mCapacity) {
755                 index -= mCapacity;
756             }
757             return index;
758         }
759     }
760 }