blob: 3d24fed600695da03d2a913fe88814b60dd15ff7 [file] [log] [blame]
Michael Wright639c8be2014-01-17 18:29:12 -08001/*
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
Jeff Brown131206b2014-04-08 17:27:14 -070017package com.android.server.display;
Michael Wright639c8be2014-01-17 18:29:12 -080018
Jeff Browna576b4d2015-04-23 19:58:06 -070019import com.android.server.EventLogTags;
Jeff Brown131206b2014-04-08 17:27:14 -070020import com.android.server.LocalServices;
Michael Wright639c8be2014-01-17 18:29:12 -080021
Justin Klaassen908b86c2016-08-08 09:18:42 -070022import android.annotation.Nullable;
Michael Wright639c8be2014-01-17 18:29:12 -080023import 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;
Jeff Browna576b4d2015-04-23 19:58:06 -070033import android.util.EventLog;
Michael Wright639c8be2014-01-17 18:29:12 -080034import android.util.MathUtils;
Michael Wright639c8be2014-01-17 18:29:12 -080035import android.util.Slog;
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -070036import android.util.Spline;
Michael Wright639c8be2014-01-17 18:29:12 -080037import android.util.TimeUtils;
38
39import java.io.PrintWriter;
Michael Wright639c8be2014-01-17 18:29:12 -080040
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.
Adrian Roosdaf7d412014-05-13 14:55:09 +020048 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
Michael Wright639c8be2014-01-17 18:29:12 -080049
Michael Wright639c8be2014-01-17 18:29:12 -080050 // 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
Jeff Browna576b4d2015-04-23 19:58:06 -070055 // 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
Michael Wright639c8be2014-01-17 18:29:12 -080059 private static final int MSG_UPDATE_AMBIENT_LUX = 1;
Jeff Browna576b4d2015-04-23 19:58:06 -070060 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
Michael Wright639c8be2014-01-17 18:29:12 -080061
Julius D'souza428aed02016-08-07 19:08:30 -070062 // Callbacks for requesting updates to the display's power state
Michael Wright639c8be2014-01-17 18:29:12 -080063 private final Callbacks mCallbacks;
64
65 // The sensor manager.
66 private final SensorManager mSensorManager;
67
68 // The light sensor, or null if not available or needed.
69 private final Sensor mLightSensor;
70
Michael Wright639c8be2014-01-17 18:29:12 -080071 // The auto-brightness spline adjustment.
72 // The brightness values have been scaled to a range of 0..1.
73 private final Spline mScreenAutoBrightnessSpline;
74
75 // The minimum and maximum screen brightnesses.
76 private final int mScreenBrightnessRangeMinimum;
77 private final int mScreenBrightnessRangeMaximum;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -070078 private final float mDozeScaleFactor;
Michael Wright639c8be2014-01-17 18:29:12 -080079
Julius D'souza5d717092016-10-24 19:26:45 -070080 // Initial light sensor event rate in milliseconds.
81 private final int mInitialLightSensorRate;
82
83 // Steady-state light sensor event rate in milliseconds.
84 private final int mNormalLightSensorRate;
85
86 // The current light sensor event rate in milliseconds.
87 private int mCurrentLightSensorRate;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -080088
89 // Stability requirements in milliseconds for accepting a new brightness level. This is used
90 // for debouncing the light sensor. Different constants are used to debounce the light sensor
91 // when adapting to brighter or darker environments. This parameter controls how quickly
92 // brightness changes occur in response to an observed change in light level that exceeds the
93 // hysteresis threshold.
94 private final long mBrighteningLightDebounceConfig;
95 private final long mDarkeningLightDebounceConfig;
96
97 // If true immediately after the screen is turned on the controller will try to adjust the
98 // brightness based on the current sensor reads. If false, the controller will collect more data
99 // and only then decide whether to change brightness.
100 private final boolean mResetAmbientLuxAfterWarmUpConfig;
101
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100102 // Period of time in which to consider light samples in milliseconds.
103 private final int mAmbientLightHorizon;
104
105 // The intercept used for the weighting calculation. This is used in order to keep all possible
106 // weighting values positive.
107 private final int mWeightingIntercept;
108
Julius D'souza428aed02016-08-07 19:08:30 -0700109 // accessor object for determining thresholds to change brightness dynamically
110 private final HysteresisLevels mDynamicHysteresis;
111
Michael Wright639c8be2014-01-17 18:29:12 -0800112 // Amount of time to delay auto-brightness after screen on while waiting for
113 // the light sensor to warm-up in milliseconds.
114 // May be 0 if no warm-up is required.
115 private int mLightSensorWarmUpTimeConfig;
116
117 // Set to true if the light sensor is enabled.
118 private boolean mLightSensorEnabled;
119
120 // The time when the light sensor was enabled.
121 private long mLightSensorEnableTime;
122
123 // The currently accepted nominal ambient light level.
124 private float mAmbientLux;
125
126 // True if mAmbientLux holds a valid value.
127 private boolean mAmbientLuxValid;
128
129 // The ambient light level threshold at which to brighten or darken the screen.
130 private float mBrighteningLuxThreshold;
131 private float mDarkeningLuxThreshold;
132
133 // The most recent light sample.
134 private float mLastObservedLux;
135
136 // The time of the most light recent sample.
137 private long mLastObservedLuxTime;
138
139 // The number of light samples collected since the light sensor was enabled.
140 private int mRecentLightSamples;
141
142 // A ring buffer containing all of the recent ambient light sensor readings.
143 private AmbientLightRingBuffer mAmbientLightRingBuffer;
144
Michael Wright103fb782016-04-22 01:03:09 -0400145 // A ring buffer containing the light sensor readings for the initial horizon period.
146 private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
147
Michael Wright639c8be2014-01-17 18:29:12 -0800148 // The handler
149 private AutomaticBrightnessHandler mHandler;
150
151 // The screen brightness level that has been chosen by the auto-brightness
152 // algorithm. The actual brightness should ramp towards this value.
153 // We preserve this value even when we stop using the light sensor so
154 // that we can quickly revert to the previous auto-brightness level
155 // while the light sensor warms up.
156 // Use -1 if there is no current auto-brightness value available.
157 private int mScreenAutoBrightness = -1;
158
159 // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
160 private float mScreenAutoBrightnessAdjustment = 0.0f;
161
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100162 // The maximum range of gamma adjustment possible using the screen
163 // auto-brightness adjustment setting.
164 private float mScreenAutoBrightnessAdjustmentMaxGamma;
165
Michael Wright639c8be2014-01-17 18:29:12 -0800166 // The last screen auto-brightness gamma. (For printing in dump() only.)
167 private float mLastScreenAutoBrightnessGamma = 1.0f;
168
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700169 // Are we going to adjust brightness while dozing.
170 private boolean mDozing;
171
Jeff Browna576b4d2015-04-23 19:58:06 -0700172 // True if we are collecting a brightness adjustment sample, along with some data
173 // for the initial state of the sample.
174 private boolean mBrightnessAdjustmentSamplePending;
175 private float mBrightnessAdjustmentSampleOldAdjustment;
176 private float mBrightnessAdjustmentSampleOldLux;
177 private int mBrightnessAdjustmentSampleOldBrightness;
178 private float mBrightnessAdjustmentSampleOldGamma;
179
Michael Wright639c8be2014-01-17 18:29:12 -0800180 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700181 SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800182 int brightnessMin, int brightnessMax, float dozeScaleFactor,
Julius D'souza5d717092016-10-24 19:26:45 -0700183 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100184 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
Julius D'souza428aed02016-08-07 19:08:30 -0700185 int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma,
186 HysteresisLevels dynamicHysteresis) {
Michael Wright639c8be2014-01-17 18:29:12 -0800187 mCallbacks = callbacks;
Michael Wright639c8be2014-01-17 18:29:12 -0800188 mSensorManager = sensorManager;
189 mScreenAutoBrightnessSpline = autoBrightnessSpline;
190 mScreenBrightnessRangeMinimum = brightnessMin;
191 mScreenBrightnessRangeMaximum = brightnessMax;
192 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700193 mDozeScaleFactor = dozeScaleFactor;
Julius D'souza5d717092016-10-24 19:26:45 -0700194 mNormalLightSensorRate = lightSensorRate;
195 mInitialLightSensorRate = initialLightSensorRate;
196 mCurrentLightSensorRate = -1;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800197 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
198 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
199 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100200 mAmbientLightHorizon = ambientLightHorizon;
201 mWeightingIntercept = ambientLightHorizon;
202 mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
Julius D'souza428aed02016-08-07 19:08:30 -0700203 mDynamicHysteresis = dynamicHysteresis;
Michael Wright639c8be2014-01-17 18:29:12 -0800204
205 mHandler = new AutomaticBrightnessHandler(looper);
Michael Wright103fb782016-04-22 01:03:09 -0400206 mAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700207 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright103fb782016-04-22 01:03:09 -0400208 mInitialHorizonAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700209 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800210
211 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
212 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
213 }
Michael Wright639c8be2014-01-17 18:29:12 -0800214 }
215
216 public int getAutomaticScreenBrightness() {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700217 if (mDozing) {
218 return (int) (mScreenAutoBrightness * mDozeScaleFactor);
219 }
Michael Wright639c8be2014-01-17 18:29:12 -0800220 return mScreenAutoBrightness;
221 }
222
Jeff Browna576b4d2015-04-23 19:58:06 -0700223 public void configure(boolean enable, float adjustment, boolean dozing,
Justin Klaassen5483cea2017-02-02 09:02:35 -0800224 boolean userInitiatedChange) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700225 // While dozing, the application processor may be suspended which will prevent us from
226 // receiving new information from the light sensor. On some devices, we may be able to
227 // switch to a wake-up light sensor instead but for now we will simply disable the sensor
228 // and hold onto the last computed screen auto brightness. We save the dozing flag for
229 // debugging purposes.
230 mDozing = dozing;
231 boolean changed = setLightSensorEnabled(enable && !dozing);
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -0700232 if (enable && !dozing && userInitiatedChange) {
233 prepareBrightnessAdjustmentSample();
234 }
Jeff Brown970d4132014-07-19 11:33:47 -0700235 changed |= setScreenAutoBrightnessAdjustment(adjustment);
236 if (changed) {
Michael Wright639c8be2014-01-17 18:29:12 -0800237 updateAutoBrightness(false /*sendUpdate*/);
238 }
239 }
240
241 public void dump(PrintWriter pw) {
242 pw.println();
243 pw.println("Automatic Brightness Controller Configuration:");
244 pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
245 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
246 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
247 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800248 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
249 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
250 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
Michael Wright639c8be2014-01-17 18:29:12 -0800251
252 pw.println();
253 pw.println("Automatic Brightness Controller State:");
254 pw.println(" mLightSensor=" + mLightSensor);
Michael Wright639c8be2014-01-17 18:29:12 -0800255 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
256 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
257 pw.println(" mAmbientLux=" + mAmbientLux);
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100258 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800259 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
260 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
261 pw.println(" mLastObservedLux=" + mLastObservedLux);
262 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
263 pw.println(" mRecentLightSamples=" + mRecentLightSamples);
264 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
Michael Wright103fb782016-04-22 01:03:09 -0400265 pw.println(" mInitialHorizonAmbientLightRingBuffer=" +
266 mInitialHorizonAmbientLightRingBuffer);
Michael Wright639c8be2014-01-17 18:29:12 -0800267 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
268 pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100269 pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
Michael Wright639c8be2014-01-17 18:29:12 -0800270 pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700271 pw.println(" mDozing=" + mDozing);
Michael Wright639c8be2014-01-17 18:29:12 -0800272 }
273
274 private boolean setLightSensorEnabled(boolean enable) {
275 if (enable) {
276 if (!mLightSensorEnabled) {
277 mLightSensorEnabled = true;
278 mLightSensorEnableTime = SystemClock.uptimeMillis();
Julius D'souza5d717092016-10-24 19:26:45 -0700279 mCurrentLightSensorRate = mInitialLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800280 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
Julius D'souza5d717092016-10-24 19:26:45 -0700281 mCurrentLightSensorRate * 1000, mHandler);
Michael Wright639c8be2014-01-17 18:29:12 -0800282 return true;
283 }
284 } else {
285 if (mLightSensorEnabled) {
286 mLightSensorEnabled = false;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800287 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800288 mRecentLightSamples = 0;
289 mAmbientLightRingBuffer.clear();
Michael Wright103fb782016-04-22 01:03:09 -0400290 mInitialHorizonAmbientLightRingBuffer.clear();
Julius D'souza5d717092016-10-24 19:26:45 -0700291 mCurrentLightSensorRate = -1;
Michael Wright639c8be2014-01-17 18:29:12 -0800292 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
293 mSensorManager.unregisterListener(mLightSensorListener);
294 }
295 }
296 return false;
297 }
298
299 private void handleLightSensorEvent(long time, float lux) {
300 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
301
Julius D'souza5d717092016-10-24 19:26:45 -0700302 if (mAmbientLightRingBuffer.size() == 0) {
303 // switch to using the steady-state sample rate after grabbing the initial light sample
304 adjustLightSensorRate(mNormalLightSensorRate);
305 }
Michael Wright639c8be2014-01-17 18:29:12 -0800306 applyLightSensorMeasurement(time, lux);
307 updateAmbientLux(time);
308 }
309
310 private void applyLightSensorMeasurement(long time, float lux) {
311 mRecentLightSamples++;
Michael Wright103fb782016-04-22 01:03:09 -0400312 // Store all of the light measurements for the intial horizon period. This is to help
313 // diagnose dim wake ups and slow responses in b/27951906.
314 if (time <= mLightSensorEnableTime + mAmbientLightHorizon) {
315 mInitialHorizonAmbientLightRingBuffer.push(time, lux);
316 }
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100317 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800318 mAmbientLightRingBuffer.push(time, lux);
319
320 // Remember this sample value.
321 mLastObservedLux = lux;
322 mLastObservedLuxTime = time;
323 }
324
Julius D'souza5d717092016-10-24 19:26:45 -0700325 private void adjustLightSensorRate(int lightSensorRate) {
326 // if the light sensor rate changed, update the sensor listener
327 if (lightSensorRate != mCurrentLightSensorRate) {
328 if (DEBUG) {
329 Slog.d(TAG, "adjustLightSensorRate: previousRate=" + mCurrentLightSensorRate
330 + ", currentRate=" + lightSensorRate);
331 }
332 mCurrentLightSensorRate = lightSensorRate;
333 mSensorManager.unregisterListener(mLightSensorListener);
334 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
335 lightSensorRate * 1000, mHandler);
336 }
337 }
338
Michael Wright639c8be2014-01-17 18:29:12 -0800339 private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
340 if (adjustment != mScreenAutoBrightnessAdjustment) {
341 mScreenAutoBrightnessAdjustment = adjustment;
342 return true;
343 }
344 return false;
345 }
346
347 private void setAmbientLux(float lux) {
348 mAmbientLux = lux;
Julius D'souza428aed02016-08-07 19:08:30 -0700349 mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux);
350 mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux);
Michael Wright639c8be2014-01-17 18:29:12 -0800351 }
352
353 private float calculateAmbientLux(long now) {
354 final int N = mAmbientLightRingBuffer.size();
355 if (N == 0) {
356 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
357 return -1;
358 }
359 float sum = 0;
360 float totalWeight = 0;
361 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
362 for (int i = N - 1; i >= 0; i--) {
363 long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
364 float weight = calculateWeight(startTime, endTime);
365 float lux = mAmbientLightRingBuffer.getLux(i);
366 if (DEBUG) {
367 Slog.d(TAG, "calculateAmbientLux: [" +
368 (startTime) + ", " +
369 (endTime) + "]: lux=" + lux + ", weight=" + weight);
370 }
371 totalWeight += weight;
372 sum += mAmbientLightRingBuffer.getLux(i) * weight;
373 endTime = startTime;
374 }
375 if (DEBUG) {
376 Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
377 ", newAmbientLux=" + (sum / totalWeight));
378 }
379 return sum / totalWeight;
380 }
381
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100382 private float calculateWeight(long startDelta, long endDelta) {
Michael Wright639c8be2014-01-17 18:29:12 -0800383 return weightIntegral(endDelta) - weightIntegral(startDelta);
384 }
385
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100386 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
Michael Wright639c8be2014-01-17 18:29:12 -0800387 // horizon we're looking at and provides a non-linear weighting for light samples.
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100388 private float weightIntegral(long x) {
389 return x * (x * 0.5f + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800390 }
391
392 private long nextAmbientLightBrighteningTransition(long time) {
393 final int N = mAmbientLightRingBuffer.size();
394 long earliestValidTime = time;
395 for (int i = N - 1; i >= 0; i--) {
396 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
397 break;
398 }
399 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
400 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800401 return earliestValidTime + mBrighteningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800402 }
403
404 private long nextAmbientLightDarkeningTransition(long time) {
405 final int N = mAmbientLightRingBuffer.size();
406 long earliestValidTime = time;
407 for (int i = N - 1; i >= 0; i--) {
408 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
409 break;
410 }
411 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
412 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800413 return earliestValidTime + mDarkeningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800414 }
415
416 private void updateAmbientLux() {
417 long time = SystemClock.uptimeMillis();
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100418 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800419 updateAmbientLux(time);
420 }
421
422 private void updateAmbientLux(long time) {
423 // If the light sensor was just turned on then immediately update our initial
424 // estimate of the current ambient light level.
425 if (!mAmbientLuxValid) {
426 final long timeWhenSensorWarmedUp =
427 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
428 if (time < timeWhenSensorWarmedUp) {
429 if (DEBUG) {
430 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
431 + "time=" + time
432 + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
433 }
434 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
435 timeWhenSensorWarmedUp);
436 return;
437 }
438 setAmbientLux(calculateAmbientLux(time));
439 mAmbientLuxValid = true;
440 if (DEBUG) {
441 Slog.d(TAG, "updateAmbientLux: Initializing: "
442 + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
443 + ", mAmbientLux=" + mAmbientLux);
444 }
445 updateAutoBrightness(true);
446 }
447
448 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
449 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
450 float ambientLux = calculateAmbientLux(time);
451
452 if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
453 || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
454 setAmbientLux(ambientLux);
455 if (DEBUG) {
456 Slog.d(TAG, "updateAmbientLux: "
457 + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
458 + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
459 + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
460 + ", mAmbientLux=" + mAmbientLux);
461 }
462 updateAutoBrightness(true);
463 nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
464 nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
465 }
466 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
467 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
468 // exceed the necessary threshold, then it's possible we'll get a transition time prior to
469 // now. Rather than continually checking to see whether the weighted lux exceeds the
470 // threshold, schedule an update for when we'd normally expect another light sample, which
471 // should be enough time to decide whether we should actually transition to the new
472 // weighted ambient lux or not.
473 nextTransitionTime =
Julius D'souza5d717092016-10-24 19:26:45 -0700474 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800475 if (DEBUG) {
476 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
477 + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
478 }
479 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
480 }
481
482 private void updateAutoBrightness(boolean sendUpdate) {
483 if (!mAmbientLuxValid) {
484 return;
485 }
486
487 float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
488 float gamma = 1.0f;
489
490 if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
491 && mScreenAutoBrightnessAdjustment != 0.0f) {
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100492 final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma,
Michael Wright639c8be2014-01-17 18:29:12 -0800493 Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
494 gamma *= adjGamma;
495 if (DEBUG) {
496 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
497 }
498 }
499
Michael Wright639c8be2014-01-17 18:29:12 -0800500 if (gamma != 1.0f) {
501 final float in = value;
502 value = MathUtils.pow(value, gamma);
503 if (DEBUG) {
504 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
505 + ", in=" + in + ", out=" + value);
506 }
507 }
508
509 int newScreenAutoBrightness =
Jeff Browna576b4d2015-04-23 19:58:06 -0700510 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
Michael Wright639c8be2014-01-17 18:29:12 -0800511 if (mScreenAutoBrightness != newScreenAutoBrightness) {
512 if (DEBUG) {
513 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
514 + mScreenAutoBrightness + ", newScreenAutoBrightness="
515 + newScreenAutoBrightness);
516 }
517
518 mScreenAutoBrightness = newScreenAutoBrightness;
519 mLastScreenAutoBrightnessGamma = gamma;
520 if (sendUpdate) {
521 mCallbacks.updateBrightness();
522 }
523 }
524 }
525
526 private int clampScreenBrightness(int value) {
527 return MathUtils.constrain(value,
528 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
529 }
530
Jeff Browna576b4d2015-04-23 19:58:06 -0700531 private void prepareBrightnessAdjustmentSample() {
532 if (!mBrightnessAdjustmentSamplePending) {
533 mBrightnessAdjustmentSamplePending = true;
534 mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment;
535 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
536 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
537 mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma;
538 } else {
539 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
540 }
541
542 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
543 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
544 }
545
546 private void cancelBrightnessAdjustmentSample() {
547 if (mBrightnessAdjustmentSamplePending) {
548 mBrightnessAdjustmentSamplePending = false;
549 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
550 }
551 }
552
553 private void collectBrightnessAdjustmentSample() {
554 if (mBrightnessAdjustmentSamplePending) {
555 mBrightnessAdjustmentSamplePending = false;
556 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
557 if (DEBUG) {
558 Slog.d(TAG, "Auto-brightness adjustment changed by user: "
559 + "adj=" + mScreenAutoBrightnessAdjustment
560 + ", lux=" + mAmbientLux
561 + ", brightness=" + mScreenAutoBrightness
562 + ", gamma=" + mLastScreenAutoBrightnessGamma
563 + ", ring=" + mAmbientLightRingBuffer);
564 }
565
566 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
567 mBrightnessAdjustmentSampleOldAdjustment,
568 mBrightnessAdjustmentSampleOldLux,
569 mBrightnessAdjustmentSampleOldBrightness,
570 mBrightnessAdjustmentSampleOldGamma,
571 mScreenAutoBrightnessAdjustment,
572 mAmbientLux,
573 mScreenAutoBrightness,
574 mLastScreenAutoBrightnessGamma);
575 }
576 }
577 }
578
Michael Wright639c8be2014-01-17 18:29:12 -0800579 private final class AutomaticBrightnessHandler extends Handler {
580 public AutomaticBrightnessHandler(Looper looper) {
581 super(looper, null, true /*async*/);
582 }
583
584 @Override
585 public void handleMessage(Message msg) {
586 switch (msg.what) {
587 case MSG_UPDATE_AMBIENT_LUX:
588 updateAmbientLux();
589 break;
Jeff Browna576b4d2015-04-23 19:58:06 -0700590
591 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
592 collectBrightnessAdjustmentSample();
593 break;
Michael Wright639c8be2014-01-17 18:29:12 -0800594 }
595 }
596 }
597
598 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
599 @Override
600 public void onSensorChanged(SensorEvent event) {
601 if (mLightSensorEnabled) {
602 final long time = SystemClock.uptimeMillis();
603 final float lux = event.values[0];
604 handleLightSensorEvent(time, lux);
605 }
606 }
607
608 @Override
609 public void onAccuracyChanged(Sensor sensor, int accuracy) {
610 // Not used.
611 }
612 };
613
Michael Wright639c8be2014-01-17 18:29:12 -0800614 /** Callbacks to request updates to the display's power state. */
615 interface Callbacks {
616 void updateBrightness();
617 }
618
Michael Wright103fb782016-04-22 01:03:09 -0400619 private static final class AmbientLightRingBuffer {
Michael Wright639c8be2014-01-17 18:29:12 -0800620 // Proportional extra capacity of the buffer beyond the expected number of light samples
621 // in the horizon
622 private static final float BUFFER_SLACK = 1.5f;
Michael Wright639c8be2014-01-17 18:29:12 -0800623 private float[] mRingLux;
624 private long[] mRingTime;
625 private int mCapacity;
626
627 // The first valid element and the next open slot.
628 // Note that if mCount is zero then there are no valid elements.
629 private int mStart;
630 private int mEnd;
631 private int mCount;
632
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100633 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
634 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800635 mRingLux = new float[mCapacity];
636 mRingTime = new long[mCapacity];
637 }
638
639 public float getLux(int index) {
640 return mRingLux[offsetOf(index)];
641 }
642
643 public long getTime(int index) {
644 return mRingTime[offsetOf(index)];
645 }
646
647 public void push(long time, float lux) {
648 int next = mEnd;
649 if (mCount == mCapacity) {
650 int newSize = mCapacity * 2;
651
652 float[] newRingLux = new float[newSize];
653 long[] newRingTime = new long[newSize];
654 int length = mCapacity - mStart;
655 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
656 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
657 if (mStart != 0) {
658 System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
659 System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
660 }
661 mRingLux = newRingLux;
662 mRingTime = newRingTime;
663
664 next = mCapacity;
665 mCapacity = newSize;
666 mStart = 0;
667 }
668 mRingTime[next] = time;
669 mRingLux[next] = lux;
670 mEnd = next + 1;
671 if (mEnd == mCapacity) {
672 mEnd = 0;
673 }
674 mCount++;
675 }
676
677 public void prune(long horizon) {
678 if (mCount == 0) {
679 return;
680 }
681
682 while (mCount > 1) {
683 int next = mStart + 1;
684 if (next >= mCapacity) {
685 next -= mCapacity;
686 }
687 if (mRingTime[next] > horizon) {
688 // Some light sensors only produce data upon a change in the ambient light
689 // levels, so we need to consider the previous measurement as the ambient light
690 // level for all points in time up until we receive a new measurement. Thus, we
691 // always want to keep the youngest element that would be removed from the
692 // buffer and just set its measurement time to the horizon time since at that
693 // point it is the ambient light level, and to remove it would be to drop a
694 // valid data point within our horizon.
695 break;
696 }
697 mStart = next;
698 mCount -= 1;
699 }
700
701 if (mRingTime[mStart] < horizon) {
702 mRingTime[mStart] = horizon;
703 }
704 }
705
706 public int size() {
707 return mCount;
708 }
709
Michael Wright639c8be2014-01-17 18:29:12 -0800710 public void clear() {
711 mStart = 0;
712 mEnd = 0;
713 mCount = 0;
714 }
715
716 @Override
717 public String toString() {
Jeff Browna576b4d2015-04-23 19:58:06 -0700718 StringBuffer buf = new StringBuffer();
719 buf.append('[');
720 for (int i = 0; i < mCount; i++) {
721 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
722 if (i != 0) {
723 buf.append(", ");
724 }
725 buf.append(getLux(i));
726 buf.append(" / ");
727 buf.append(next - getTime(i));
728 buf.append("ms");
Michael Wright639c8be2014-01-17 18:29:12 -0800729 }
Jeff Browna576b4d2015-04-23 19:58:06 -0700730 buf.append(']');
731 return buf.toString();
Michael Wright639c8be2014-01-17 18:29:12 -0800732 }
733
734 private int offsetOf(int index) {
735 if (index >= mCount || index < 0) {
736 throw new ArrayIndexOutOfBoundsException(index);
737 }
738 index += mStart;
739 if (index >= mCapacity) {
740 index -= mCapacity;
741 }
742 return index;
743 }
744 }
745}