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