OSDN Git Service

Increase brightness faster if ambient light is brightening quickly.
[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     // 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;
62
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;
67
68     // Specifies the maximum magnitude of the time of day adjustment.
69     private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f;
70
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;
74
75     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
76     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
77
78     // Callbacks for requesting updates to the the display's power state
79     private final Callbacks mCallbacks;
80
81     // The sensor manager.
82     private final SensorManager mSensorManager;
83
84     // The light sensor, or null if not available or needed.
85     private final Sensor mLightSensor;
86
87     // The twilight service.
88     private final TwilightManager mTwilight;
89
90     // The auto-brightness spline adjustment.
91     // The brightness values have been scaled to a range of 0..1.
92     private final Spline mScreenAutoBrightnessSpline;
93
94     // The minimum and maximum screen brightnesses.
95     private final int mScreenBrightnessRangeMinimum;
96     private final int mScreenBrightnessRangeMaximum;
97     private final float mDozeScaleFactor;
98
99     // Light sensor event rate in milliseconds.
100     private final int mLightSensorRate;
101
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;
110
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;
115
116     // Period of time in which to consider light samples in milliseconds.
117     private final int mAmbientLightHorizon;
118
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;
122
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;
127
128     // Set to true if the light sensor is enabled.
129     private boolean mLightSensorEnabled;
130
131     // The time when the light sensor was enabled.
132     private long mLightSensorEnableTime;
133
134     // The currently accepted nominal ambient light level.
135     private float mAmbientLux;
136
137     // True if mAmbientLux holds a valid value.
138     private boolean mAmbientLuxValid;
139
140     // The ambient light level threshold at which to brighten or darken the screen.
141     private float mBrighteningLuxThreshold;
142     private float mDarkeningLuxThreshold;
143
144     // The most recent light sample.
145     private float mLastObservedLux;
146
147     // The time of the most light recent sample.
148     private long mLastObservedLuxTime;
149
150     // The number of light samples collected since the light sensor was enabled.
151     private int mRecentLightSamples;
152
153     // A ring buffer containing all of the recent ambient light sensor readings.
154     private AmbientLightRingBuffer mAmbientLightRingBuffer;
155
156     // A ring buffer containing the light sensor readings for the initial horizon period.
157     private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
158
159     // The handler
160     private AutomaticBrightnessHandler mHandler;
161
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;
169
170     // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
171     private float mScreenAutoBrightnessAdjustment = 0.0f;
172
173     // The maximum range of gamma adjustment possible using the screen
174     // auto-brightness adjustment setting.
175     private float mScreenAutoBrightnessAdjustmentMaxGamma;
176
177     // The last screen auto-brightness gamma.  (For printing in dump() only.)
178     private float mLastScreenAutoBrightnessGamma = 1.0f;
179
180     // Are we going to adjust brightness while dozing.
181     private boolean mDozing;
182
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;
190
191     private boolean mUseTwilight;
192
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;
216
217         mHandler = new AutomaticBrightnessHandler(looper);
218         mAmbientLightRingBuffer =
219             new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
220         mInitialHorizonAmbientLightRingBuffer =
221             new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
222
223         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
224             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
225         }
226     }
227
228     public int getAutomaticScreenBrightness() {
229         if (mDozing) {
230             return (int) (mScreenAutoBrightness * mDozeScaleFactor);
231         }
232         return mScreenAutoBrightness;
233     }
234
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.
242         mDozing = dozing;
243         boolean changed = setLightSensorEnabled(enable && !dozing);
244         changed |= setScreenAutoBrightnessAdjustment(adjustment);
245         changed |= setUseTwilight(useTwilight);
246         if (changed) {
247             updateAutoBrightness(false /*sendUpdate*/);
248         }
249         if (enable && !dozing && userInitiatedChange) {
250             prepareBrightnessAdjustmentSample();
251         }
252     }
253
254     private boolean setUseTwilight(boolean useTwilight) {
255         if (mUseTwilight == useTwilight) return false;
256         if (useTwilight) {
257             mTwilight.registerListener(mTwilightListener, mHandler);
258         } else {
259             mTwilight.unregisterListener(mTwilightListener);
260         }
261         mUseTwilight = useTwilight;
262         return true;
263     }
264
265     public void dump(PrintWriter pw) {
266         pw.println();
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);
276
277         pw.println();
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);
298     }
299
300     private boolean setLightSensorEnabled(boolean enable) {
301         if (enable) {
302             if (!mLightSensorEnabled) {
303                 mLightSensorEnabled = true;
304                 mLightSensorEnableTime = SystemClock.uptimeMillis();
305                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
306                         mLightSensorRate * 1000, mHandler);
307                 return true;
308             }
309         } else {
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);
318             }
319         }
320         return false;
321     }
322
323     private void handleLightSensorEvent(long time, float lux) {
324         mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
325
326         if (DEBUG) Slog.d(TAG, "handleLightSensorEvent: time=" + time + ", lux=" + lux);
327         applyLightSensorMeasurement(time, lux);
328         updateAmbientLux(time);
329     }
330
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);
337         }
338         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
339         mAmbientLightRingBuffer.push(time, lux);
340
341         // Remember this sample value.
342         mLastObservedLux = lux;
343         mLastObservedLuxTime = time;
344     }
345
346     private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
347         if (adjustment != mScreenAutoBrightnessAdjustment) {
348             mScreenAutoBrightnessAdjustment = adjustment;
349             return true;
350         }
351         return false;
352     }
353
354     private void setAmbientLux(float lux) {
355         mAmbientLux = lux;
356         mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
357         mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
358     }
359
360     private float calculateAmbientLux(long now) {
361         final int N = mAmbientLightRingBuffer.size();
362         if (N == 0) {
363             Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
364             return -1;
365         }
366         float sum = 0;
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);
373             if (DEBUG) {
374                 Slog.d(TAG, "calculateAmbientLux: [" +
375                         (startTime) + ", " +
376                         (endTime) + "]: lux=" + lux + ", weight=" + weight);
377             }
378             totalWeight += weight;
379             sum += mAmbientLightRingBuffer.getLux(i) * weight;
380             endTime = startTime;
381         }
382         if (DEBUG) {
383             Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
384                     ", newAmbientLux=" + (sum / totalWeight));
385         }
386         return sum / totalWeight;
387     }
388
389     private float calculateWeight(long startDelta, long endDelta) {
390         return weightIntegral(endDelta) - weightIntegral(startDelta);
391     }
392
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);
397     }
398
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) {
404                 break;
405             }
406             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
407         }
408
409         long debounceDelay = mLastObservedLux - ambientLux > BRIGHTENING_FAST_THRESHOLD
410                 ? mBrighteningLightFastDebounceConfig : mBrighteningLightDebounceConfig;
411         return earliestValidTime + debounceDelay;
412     }
413
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) {
419                 break;
420             }
421             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
422         }
423         return earliestValidTime + mDarkeningLightDebounceConfig;
424     }
425
426     private void updateAmbientLux() {
427         long time = SystemClock.uptimeMillis();
428         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
429         updateAmbientLux(time);
430     }
431
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) {
439                 if (DEBUG) {
440                     Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: "
441                             + "time=" + time
442                             + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
443                 }
444                 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
445                         timeWhenSensorWarmedUp);
446                 return;
447             }
448             setAmbientLux(calculateAmbientLux(time));
449             mAmbientLuxValid = true;
450             if (DEBUG) {
451                 Slog.d(TAG, "updateAmbientLux: Initializing: "
452                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
453                         + ", mAmbientLux=" + mAmbientLux);
454             }
455             updateAutoBrightness(true);
456         }
457
458         float ambientLux = calculateAmbientLux(time);
459         long nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
460         long nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
461
462         if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
463                 || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
464             if (DEBUG) {
465                 Slog.d(TAG, "updateAmbientLux: "
466                         + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
467                         + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
468                         + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
469                         + ", mAmbientLux=" + mAmbientLux);
470             }
471             setAmbientLux(ambientLux);
472             updateAutoBrightness(true);
473             nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
474             nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
475         }
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.
483         nextTransitionTime =
484                 nextTransitionTime > time ? nextTransitionTime : time + mLightSensorRate;
485         if (DEBUG) {
486             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
487                     + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
488         }
489         mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
490     }
491
492     private void updateAutoBrightness(boolean sendUpdate) {
493         if (!mAmbientLuxValid) {
494             return;
495         }
496
497         float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
498         float gamma = 1.0f;
499
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)));
504             gamma *= adjGamma;
505             if (DEBUG) {
506                 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
507             }
508         }
509
510         if (mUseTwilight) {
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;
515                 if (DEBUG) {
516                     Slog.d(TAG, "updateAutoBrightness: twilight amount=" + state.getAmount());
517                 }
518             }
519         }
520
521         if (gamma != 1.0f) {
522             final float in = value;
523             value = MathUtils.pow(value, gamma);
524             if (DEBUG) {
525                 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
526                         + ", in=" + in + ", out=" + value);
527             }
528         }
529
530         int newScreenAutoBrightness =
531                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
532         if (mScreenAutoBrightness != newScreenAutoBrightness) {
533             if (DEBUG) {
534                 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
535                         + mScreenAutoBrightness + ", newScreenAutoBrightness="
536                         + newScreenAutoBrightness);
537             }
538
539             mScreenAutoBrightness = newScreenAutoBrightness;
540             mLastScreenAutoBrightnessGamma = gamma;
541             if (sendUpdate) {
542                 mCallbacks.updateBrightness();
543             }
544         }
545     }
546
547     private int clampScreenBrightness(int value) {
548         return MathUtils.constrain(value,
549                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
550     }
551
552     private void prepareBrightnessAdjustmentSample() {
553         if (!mBrightnessAdjustmentSamplePending) {
554             mBrightnessAdjustmentSamplePending = true;
555             mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment;
556             mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
557             mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
558             mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma;
559         } else {
560             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
561         }
562
563         mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
564                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
565     }
566
567     private void cancelBrightnessAdjustmentSample() {
568         if (mBrightnessAdjustmentSamplePending) {
569             mBrightnessAdjustmentSamplePending = false;
570             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
571         }
572     }
573
574     private void collectBrightnessAdjustmentSample() {
575         if (mBrightnessAdjustmentSamplePending) {
576             mBrightnessAdjustmentSamplePending = false;
577             if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
578                 if (DEBUG) {
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);
585                 }
586
587                 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
588                         mBrightnessAdjustmentSampleOldAdjustment,
589                         mBrightnessAdjustmentSampleOldLux,
590                         mBrightnessAdjustmentSampleOldBrightness,
591                         mBrightnessAdjustmentSampleOldGamma,
592                         mScreenAutoBrightnessAdjustment,
593                         mAmbientLux,
594                         mScreenAutoBrightness,
595                         mLastScreenAutoBrightnessGamma);
596             }
597         }
598     }
599
600     private final class AutomaticBrightnessHandler extends Handler {
601         public AutomaticBrightnessHandler(Looper looper) {
602             super(looper, null, true /*async*/);
603         }
604
605         @Override
606         public void handleMessage(Message msg) {
607             switch (msg.what) {
608                 case MSG_UPDATE_AMBIENT_LUX:
609                     updateAmbientLux();
610                     break;
611
612                 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
613                     collectBrightnessAdjustmentSample();
614                     break;
615             }
616         }
617     }
618
619     private final SensorEventListener mLightSensorListener = new SensorEventListener() {
620         @Override
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);
626             }
627         }
628
629         @Override
630         public void onAccuracyChanged(Sensor sensor, int accuracy) {
631             // Not used.
632         }
633     };
634
635     private final TwilightListener mTwilightListener = new TwilightListener() {
636         @Override
637         public void onTwilightStateChanged() {
638             updateAutoBrightness(true /*sendUpdate*/);
639         }
640     };
641
642     /** Callbacks to request updates to the display's power state. */
643     interface Callbacks {
644         void updateBrightness();
645     }
646
647     private static final class AmbientLightRingBuffer {
648         // Proportional extra capacity of the buffer beyond the expected number of light samples
649         // in the horizon
650         private static final float BUFFER_SLACK = 1.5f;
651         private float[] mRingLux;
652         private long[] mRingTime;
653         private int mCapacity;
654
655         // The first valid element and the next open slot.
656         // Note that if mCount is zero then there are no valid elements.
657         private int mStart;
658         private int mEnd;
659         private int mCount;
660
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];
665         }
666
667         public float getLux(int index) {
668             return mRingLux[offsetOf(index)];
669         }
670
671         public long getTime(int index) {
672             return mRingTime[offsetOf(index)];
673         }
674
675         public void push(long time, float lux) {
676             int next = mEnd;
677             if (mCount == mCapacity) {
678                 int newSize = mCapacity * 2;
679
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);
685                 if (mStart != 0) {
686                     System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
687                     System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
688                 }
689                 mRingLux = newRingLux;
690                 mRingTime = newRingTime;
691
692                 next = mCapacity;
693                 mCapacity = newSize;
694                 mStart = 0;
695             }
696             mRingTime[next] = time;
697             mRingLux[next] = lux;
698             mEnd = next + 1;
699             if (mEnd == mCapacity) {
700                 mEnd = 0;
701             }
702             mCount++;
703         }
704
705         public void prune(long horizon) {
706             if (mCount == 0) {
707                 return;
708             }
709
710             while (mCount > 1) {
711                 int next = mStart + 1;
712                 if (next >= mCapacity) {
713                     next -= mCapacity;
714                 }
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.
723                     break;
724                 }
725                 mStart = next;
726                 mCount -= 1;
727             }
728
729             if (mRingTime[mStart] < horizon) {
730                 mRingTime[mStart] = horizon;
731             }
732         }
733
734         public int size() {
735             return mCount;
736         }
737
738         public void clear() {
739             mStart = 0;
740             mEnd = 0;
741             mCount = 0;
742         }
743
744         @Override
745         public String toString() {
746             StringBuffer buf = new StringBuffer();
747             buf.append('[');
748             for (int i = 0; i < mCount; i++) {
749                 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
750                 if (i != 0) {
751                     buf.append(", ");
752                 }
753                 buf.append(getLux(i));
754                 buf.append(" / ");
755                 buf.append(next - getTime(i));
756                 buf.append("ms");
757             }
758             buf.append(']');
759             return buf.toString();
760         }
761
762         private int offsetOf(int index) {
763             if (index >= mCount || index < 0) {
764                 throw new ArrayIndexOutOfBoundsException(index);
765             }
766             index += mStart;
767             if (index >= mCapacity) {
768                 index -= mCapacity;
769             }
770             return index;
771         }
772     }
773 }