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