blob: 9a6e609445a510d6ef76c24194014d507958a2fe [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;
Michael Wrightb0a1d3d2017-09-22 15:05:02 +010032import android.os.Trace;
Michael Wright639c8be2014-01-17 18:29:12 -080033import android.text.format.DateUtils;
Jeff Browna576b4d2015-04-23 19:58:06 -070034import android.util.EventLog;
Michael Wright639c8be2014-01-17 18:29:12 -080035import android.util.MathUtils;
Michael Wright639c8be2014-01-17 18:29:12 -080036import android.util.Slog;
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -070037import android.util.Spline;
Michael Wright639c8be2014-01-17 18:29:12 -080038import android.util.TimeUtils;
39
40import java.io.PrintWriter;
Michael Wright639c8be2014-01-17 18:29:12 -080041
42class AutomaticBrightnessController {
43 private static final String TAG = "AutomaticBrightnessController";
44
45 private static final boolean DEBUG = false;
46 private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
47
48 // If true, enables the use of the screen auto-brightness adjustment setting.
Adrian Roosdaf7d412014-05-13 14:55:09 +020049 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
Michael Wright639c8be2014-01-17 18:29:12 -080050
Michael Wright639c8be2014-01-17 18:29:12 -080051 // How long the current sensor reading is assumed to be valid beyond the current time.
52 // This provides a bit of prediction, as well as ensures that the weight for the last sample is
53 // non-zero, which in turn ensures that the total weight is non-zero.
54 private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
55
Jeff Browna576b4d2015-04-23 19:58:06 -070056 // Debounce for sampling user-initiated changes in display brightness to ensure
57 // the user is satisfied with the result before storing the sample.
58 private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
59
Michael Wright639c8be2014-01-17 18:29:12 -080060 private static final int MSG_UPDATE_AMBIENT_LUX = 1;
Jeff Browna576b4d2015-04-23 19:58:06 -070061 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
Michael Wright639c8be2014-01-17 18:29:12 -080062
Michael Wright0a933142017-08-16 20:38:21 +010063 // Length of the ambient light horizon used to calculate the long term estimate of ambient
64 // light.
65 private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
66
67 // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
68 private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
69
Julius D'souza428aed02016-08-07 19:08:30 -070070 // Callbacks for requesting updates to the display's power state
Michael Wright639c8be2014-01-17 18:29:12 -080071 private final Callbacks mCallbacks;
72
73 // The sensor manager.
74 private final SensorManager mSensorManager;
75
76 // The light sensor, or null if not available or needed.
77 private final Sensor mLightSensor;
78
Michael Wright639c8be2014-01-17 18:29:12 -080079 // The auto-brightness spline adjustment.
80 // The brightness values have been scaled to a range of 0..1.
81 private final Spline mScreenAutoBrightnessSpline;
82
83 // The minimum and maximum screen brightnesses.
84 private final int mScreenBrightnessRangeMinimum;
85 private final int mScreenBrightnessRangeMaximum;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -070086 private final float mDozeScaleFactor;
Michael Wright639c8be2014-01-17 18:29:12 -080087
Julius D'souza5d717092016-10-24 19:26:45 -070088 // Initial light sensor event rate in milliseconds.
89 private final int mInitialLightSensorRate;
90
91 // Steady-state light sensor event rate in milliseconds.
92 private final int mNormalLightSensorRate;
93
94 // The current light sensor event rate in milliseconds.
95 private int mCurrentLightSensorRate;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -080096
97 // Stability requirements in milliseconds for accepting a new brightness level. This is used
98 // for debouncing the light sensor. Different constants are used to debounce the light sensor
99 // when adapting to brighter or darker environments. This parameter controls how quickly
100 // brightness changes occur in response to an observed change in light level that exceeds the
101 // hysteresis threshold.
102 private final long mBrighteningLightDebounceConfig;
103 private final long mDarkeningLightDebounceConfig;
104
105 // If true immediately after the screen is turned on the controller will try to adjust the
106 // brightness based on the current sensor reads. If false, the controller will collect more data
107 // and only then decide whether to change brightness.
108 private final boolean mResetAmbientLuxAfterWarmUpConfig;
109
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100110 // Period of time in which to consider light samples in milliseconds.
111 private final int mAmbientLightHorizon;
112
113 // The intercept used for the weighting calculation. This is used in order to keep all possible
114 // weighting values positive.
115 private final int mWeightingIntercept;
116
Julius D'souza428aed02016-08-07 19:08:30 -0700117 // accessor object for determining thresholds to change brightness dynamically
118 private final HysteresisLevels mDynamicHysteresis;
119
Michael Wright639c8be2014-01-17 18:29:12 -0800120 // Amount of time to delay auto-brightness after screen on while waiting for
121 // the light sensor to warm-up in milliseconds.
122 // May be 0 if no warm-up is required.
123 private int mLightSensorWarmUpTimeConfig;
124
125 // Set to true if the light sensor is enabled.
126 private boolean mLightSensorEnabled;
127
128 // The time when the light sensor was enabled.
129 private long mLightSensorEnableTime;
130
131 // The currently accepted nominal ambient light level.
132 private float mAmbientLux;
133
134 // True if mAmbientLux holds a valid value.
135 private boolean mAmbientLuxValid;
136
137 // The ambient light level threshold at which to brighten or darken the screen.
138 private float mBrighteningLuxThreshold;
139 private float mDarkeningLuxThreshold;
140
141 // The most recent light sample.
142 private float mLastObservedLux;
143
144 // The time of the most light recent sample.
145 private long mLastObservedLuxTime;
146
147 // The number of light samples collected since the light sensor was enabled.
148 private int mRecentLightSamples;
149
150 // A ring buffer containing all of the recent ambient light sensor readings.
151 private AmbientLightRingBuffer mAmbientLightRingBuffer;
152
Michael Wright103fb782016-04-22 01:03:09 -0400153 // A ring buffer containing the light sensor readings for the initial horizon period.
154 private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer;
155
Michael Wright639c8be2014-01-17 18:29:12 -0800156 // The handler
157 private AutomaticBrightnessHandler mHandler;
158
159 // The screen brightness level that has been chosen by the auto-brightness
160 // algorithm. The actual brightness should ramp towards this value.
161 // We preserve this value even when we stop using the light sensor so
162 // that we can quickly revert to the previous auto-brightness level
163 // while the light sensor warms up.
164 // Use -1 if there is no current auto-brightness value available.
165 private int mScreenAutoBrightness = -1;
166
167 // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
168 private float mScreenAutoBrightnessAdjustment = 0.0f;
169
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100170 // The maximum range of gamma adjustment possible using the screen
171 // auto-brightness adjustment setting.
172 private float mScreenAutoBrightnessAdjustmentMaxGamma;
173
Michael Wright639c8be2014-01-17 18:29:12 -0800174 // The last screen auto-brightness gamma. (For printing in dump() only.)
175 private float mLastScreenAutoBrightnessGamma = 1.0f;
176
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700177 // Are we going to adjust brightness while dozing.
178 private boolean mDozing;
179
Jeff Browna576b4d2015-04-23 19:58:06 -0700180 // True if we are collecting a brightness adjustment sample, along with some data
181 // for the initial state of the sample.
182 private boolean mBrightnessAdjustmentSamplePending;
183 private float mBrightnessAdjustmentSampleOldAdjustment;
184 private float mBrightnessAdjustmentSampleOldLux;
185 private int mBrightnessAdjustmentSampleOldBrightness;
186 private float mBrightnessAdjustmentSampleOldGamma;
187
Michael Wright639c8be2014-01-17 18:29:12 -0800188 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700189 SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800190 int brightnessMin, int brightnessMax, float dozeScaleFactor,
Julius D'souza5d717092016-10-24 19:26:45 -0700191 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100192 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
Julius D'souza428aed02016-08-07 19:08:30 -0700193 int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma,
194 HysteresisLevels dynamicHysteresis) {
Michael Wright639c8be2014-01-17 18:29:12 -0800195 mCallbacks = callbacks;
Michael Wright639c8be2014-01-17 18:29:12 -0800196 mSensorManager = sensorManager;
197 mScreenAutoBrightnessSpline = autoBrightnessSpline;
198 mScreenBrightnessRangeMinimum = brightnessMin;
199 mScreenBrightnessRangeMaximum = brightnessMax;
200 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700201 mDozeScaleFactor = dozeScaleFactor;
Julius D'souza5d717092016-10-24 19:26:45 -0700202 mNormalLightSensorRate = lightSensorRate;
203 mInitialLightSensorRate = initialLightSensorRate;
204 mCurrentLightSensorRate = -1;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800205 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
206 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
207 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100208 mAmbientLightHorizon = ambientLightHorizon;
209 mWeightingIntercept = ambientLightHorizon;
210 mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
Julius D'souza428aed02016-08-07 19:08:30 -0700211 mDynamicHysteresis = dynamicHysteresis;
Michael Wright639c8be2014-01-17 18:29:12 -0800212
213 mHandler = new AutomaticBrightnessHandler(looper);
Michael Wright103fb782016-04-22 01:03:09 -0400214 mAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700215 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright103fb782016-04-22 01:03:09 -0400216 mInitialHorizonAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700217 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800218
219 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
220 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
221 }
Michael Wright639c8be2014-01-17 18:29:12 -0800222 }
223
224 public int getAutomaticScreenBrightness() {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700225 if (mDozing) {
226 return (int) (mScreenAutoBrightness * mDozeScaleFactor);
227 }
Michael Wright639c8be2014-01-17 18:29:12 -0800228 return mScreenAutoBrightness;
229 }
230
Jeff Browna576b4d2015-04-23 19:58:06 -0700231 public void configure(boolean enable, float adjustment, boolean dozing,
Justin Klaassen5483cea2017-02-02 09:02:35 -0800232 boolean userInitiatedChange) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700233 // While dozing, the application processor may be suspended which will prevent us from
234 // receiving new information from the light sensor. On some devices, we may be able to
235 // switch to a wake-up light sensor instead but for now we will simply disable the sensor
236 // and hold onto the last computed screen auto brightness. We save the dozing flag for
237 // debugging purposes.
238 mDozing = dozing;
239 boolean changed = setLightSensorEnabled(enable && !dozing);
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -0700240 if (enable && !dozing && userInitiatedChange) {
241 prepareBrightnessAdjustmentSample();
242 }
Jeff Brown970d4132014-07-19 11:33:47 -0700243 changed |= setScreenAutoBrightnessAdjustment(adjustment);
244 if (changed) {
Michael Wright639c8be2014-01-17 18:29:12 -0800245 updateAutoBrightness(false /*sendUpdate*/);
246 }
247 }
248
249 public void dump(PrintWriter pw) {
250 pw.println();
251 pw.println("Automatic Brightness Controller Configuration:");
252 pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
253 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
254 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
255 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800256 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
257 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
258 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
Michael Wright639c8be2014-01-17 18:29:12 -0800259
260 pw.println();
261 pw.println("Automatic Brightness Controller State:");
262 pw.println(" mLightSensor=" + mLightSensor);
Michael Wright639c8be2014-01-17 18:29:12 -0800263 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
264 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
265 pw.println(" mAmbientLux=" + mAmbientLux);
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100266 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800267 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
268 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
269 pw.println(" mLastObservedLux=" + mLastObservedLux);
270 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
271 pw.println(" mRecentLightSamples=" + mRecentLightSamples);
272 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
Michael Wright103fb782016-04-22 01:03:09 -0400273 pw.println(" mInitialHorizonAmbientLightRingBuffer=" +
274 mInitialHorizonAmbientLightRingBuffer);
Michael Wright639c8be2014-01-17 18:29:12 -0800275 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
276 pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100277 pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
Michael Wright639c8be2014-01-17 18:29:12 -0800278 pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700279 pw.println(" mDozing=" + mDozing);
Michael Wright639c8be2014-01-17 18:29:12 -0800280 }
281
282 private boolean setLightSensorEnabled(boolean enable) {
283 if (enable) {
284 if (!mLightSensorEnabled) {
285 mLightSensorEnabled = true;
286 mLightSensorEnableTime = SystemClock.uptimeMillis();
Julius D'souza5d717092016-10-24 19:26:45 -0700287 mCurrentLightSensorRate = mInitialLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800288 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
Julius D'souza5d717092016-10-24 19:26:45 -0700289 mCurrentLightSensorRate * 1000, mHandler);
Michael Wright639c8be2014-01-17 18:29:12 -0800290 return true;
291 }
292 } else {
293 if (mLightSensorEnabled) {
294 mLightSensorEnabled = false;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800295 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800296 mRecentLightSamples = 0;
297 mAmbientLightRingBuffer.clear();
Michael Wright103fb782016-04-22 01:03:09 -0400298 mInitialHorizonAmbientLightRingBuffer.clear();
Julius D'souza5d717092016-10-24 19:26:45 -0700299 mCurrentLightSensorRate = -1;
Michael Wright639c8be2014-01-17 18:29:12 -0800300 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
301 mSensorManager.unregisterListener(mLightSensorListener);
302 }
303 }
304 return false;
305 }
306
307 private void handleLightSensorEvent(long time, float lux) {
Michael Wrightb0a1d3d2017-09-22 15:05:02 +0100308 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
Michael Wright639c8be2014-01-17 18:29:12 -0800309 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
310
Julius D'souza5d717092016-10-24 19:26:45 -0700311 if (mAmbientLightRingBuffer.size() == 0) {
312 // switch to using the steady-state sample rate after grabbing the initial light sample
313 adjustLightSensorRate(mNormalLightSensorRate);
314 }
Michael Wright639c8be2014-01-17 18:29:12 -0800315 applyLightSensorMeasurement(time, lux);
316 updateAmbientLux(time);
317 }
318
319 private void applyLightSensorMeasurement(long time, float lux) {
320 mRecentLightSamples++;
Michael Wright103fb782016-04-22 01:03:09 -0400321 // Store all of the light measurements for the intial horizon period. This is to help
322 // diagnose dim wake ups and slow responses in b/27951906.
323 if (time <= mLightSensorEnableTime + mAmbientLightHorizon) {
324 mInitialHorizonAmbientLightRingBuffer.push(time, lux);
325 }
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100326 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800327 mAmbientLightRingBuffer.push(time, lux);
328
329 // Remember this sample value.
330 mLastObservedLux = lux;
331 mLastObservedLuxTime = time;
332 }
333
Julius D'souza5d717092016-10-24 19:26:45 -0700334 private void adjustLightSensorRate(int lightSensorRate) {
335 // if the light sensor rate changed, update the sensor listener
336 if (lightSensorRate != mCurrentLightSensorRate) {
337 if (DEBUG) {
338 Slog.d(TAG, "adjustLightSensorRate: previousRate=" + mCurrentLightSensorRate
339 + ", currentRate=" + lightSensorRate);
340 }
341 mCurrentLightSensorRate = lightSensorRate;
342 mSensorManager.unregisterListener(mLightSensorListener);
343 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
344 lightSensorRate * 1000, mHandler);
345 }
346 }
347
Michael Wright639c8be2014-01-17 18:29:12 -0800348 private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
349 if (adjustment != mScreenAutoBrightnessAdjustment) {
350 mScreenAutoBrightnessAdjustment = adjustment;
351 return true;
352 }
353 return false;
354 }
355
356 private void setAmbientLux(float lux) {
Michael Wright0a933142017-08-16 20:38:21 +0100357 if (DEBUG) {
358 Slog.d(TAG, "setAmbientLux(" + lux + ")");
359 }
Michael Wright639c8be2014-01-17 18:29:12 -0800360 mAmbientLux = lux;
Julius D'souza428aed02016-08-07 19:08:30 -0700361 mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux);
362 mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux);
Michael Wright639c8be2014-01-17 18:29:12 -0800363 }
364
Michael Wright0a933142017-08-16 20:38:21 +0100365 private float calculateAmbientLux(long now, long horizon) {
366 if (DEBUG) {
367 Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
368 }
Michael Wright639c8be2014-01-17 18:29:12 -0800369 final int N = mAmbientLightRingBuffer.size();
370 if (N == 0) {
371 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
372 return -1;
373 }
Michael Wright0a933142017-08-16 20:38:21 +0100374
375 // Find the first measurement that is just outside of the horizon.
376 int endIndex = 0;
377 final long horizonStartTime = now - horizon;
378 for (int i = 0; i < N-1; i++) {
379 if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
380 endIndex++;
381 } else {
382 break;
383 }
384 }
385 if (DEBUG) {
386 Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=("
387 + mAmbientLightRingBuffer.getTime(endIndex) + ", "
388 + mAmbientLightRingBuffer.getLux(endIndex)
389 + ")");
390 }
Michael Wright639c8be2014-01-17 18:29:12 -0800391 float sum = 0;
392 float totalWeight = 0;
393 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
Michael Wright0a933142017-08-16 20:38:21 +0100394 for (int i = N - 1; i >= endIndex; i--) {
395 long eventTime = mAmbientLightRingBuffer.getTime(i);
396 if (i == endIndex && eventTime < horizonStartTime) {
397 // If we're at the final value, make sure we only consider the part of the sample
398 // within our desired horizon.
399 eventTime = horizonStartTime;
400 }
401 final long startTime = eventTime - now;
Michael Wright639c8be2014-01-17 18:29:12 -0800402 float weight = calculateWeight(startTime, endTime);
403 float lux = mAmbientLightRingBuffer.getLux(i);
404 if (DEBUG) {
405 Slog.d(TAG, "calculateAmbientLux: [" +
406 (startTime) + ", " +
407 (endTime) + "]: lux=" + lux + ", weight=" + weight);
408 }
409 totalWeight += weight;
410 sum += mAmbientLightRingBuffer.getLux(i) * weight;
411 endTime = startTime;
412 }
413 if (DEBUG) {
414 Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
415 ", newAmbientLux=" + (sum / totalWeight));
416 }
417 return sum / totalWeight;
418 }
419
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100420 private float calculateWeight(long startDelta, long endDelta) {
Michael Wright639c8be2014-01-17 18:29:12 -0800421 return weightIntegral(endDelta) - weightIntegral(startDelta);
422 }
423
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100424 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
Michael Wright639c8be2014-01-17 18:29:12 -0800425 // horizon we're looking at and provides a non-linear weighting for light samples.
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100426 private float weightIntegral(long x) {
427 return x * (x * 0.5f + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800428 }
429
430 private long nextAmbientLightBrighteningTransition(long time) {
431 final int N = mAmbientLightRingBuffer.size();
432 long earliestValidTime = time;
433 for (int i = N - 1; i >= 0; i--) {
434 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
435 break;
436 }
437 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
438 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800439 return earliestValidTime + mBrighteningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800440 }
441
442 private long nextAmbientLightDarkeningTransition(long time) {
443 final int N = mAmbientLightRingBuffer.size();
444 long earliestValidTime = time;
445 for (int i = N - 1; i >= 0; i--) {
446 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
447 break;
448 }
449 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
450 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800451 return earliestValidTime + mDarkeningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800452 }
453
454 private void updateAmbientLux() {
455 long time = SystemClock.uptimeMillis();
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100456 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800457 updateAmbientLux(time);
458 }
459
460 private void updateAmbientLux(long time) {
461 // If the light sensor was just turned on then immediately update our initial
462 // estimate of the current ambient light level.
463 if (!mAmbientLuxValid) {
464 final long timeWhenSensorWarmedUp =
465 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
466 if (time < timeWhenSensorWarmedUp) {
467 if (DEBUG) {
468 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
469 + "time=" + time
470 + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
471 }
472 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
473 timeWhenSensorWarmedUp);
474 return;
475 }
Michael Wright0a933142017-08-16 20:38:21 +0100476 setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
Michael Wright639c8be2014-01-17 18:29:12 -0800477 mAmbientLuxValid = true;
478 if (DEBUG) {
479 Slog.d(TAG, "updateAmbientLux: Initializing: "
480 + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
481 + ", mAmbientLux=" + mAmbientLux);
482 }
483 updateAutoBrightness(true);
484 }
485
486 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
487 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
Michael Wright0a933142017-08-16 20:38:21 +0100488 // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
489 // change in lighting conditions, and a fast ambient lux to determine what the new
490 // brightness situation is since the slow lux can be quite slow to converge.
491 //
492 // Note that both values need to be checked for sufficient change before updating the
493 // proposed ambient light value since the slow value might be sufficiently far enough away
494 // from the fast value to cause a recalculation while its actually just converging on
495 // the fast value still.
496 float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
497 float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
Michael Wright639c8be2014-01-17 18:29:12 -0800498
Michael Wright0a933142017-08-16 20:38:21 +0100499 if (slowAmbientLux >= mBrighteningLuxThreshold &&
500 fastAmbientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
501 || slowAmbientLux <= mDarkeningLuxThreshold
502 && fastAmbientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
503 setAmbientLux(fastAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800504 if (DEBUG) {
505 Slog.d(TAG, "updateAmbientLux: "
Michael Wright0a933142017-08-16 20:38:21 +0100506 + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
Michael Wright639c8be2014-01-17 18:29:12 -0800507 + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
508 + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
509 + ", mAmbientLux=" + mAmbientLux);
510 }
511 updateAutoBrightness(true);
512 nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
513 nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
514 }
515 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
516 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
517 // exceed the necessary threshold, then it's possible we'll get a transition time prior to
518 // now. Rather than continually checking to see whether the weighted lux exceeds the
519 // threshold, schedule an update for when we'd normally expect another light sample, which
520 // should be enough time to decide whether we should actually transition to the new
521 // weighted ambient lux or not.
522 nextTransitionTime =
Julius D'souza5d717092016-10-24 19:26:45 -0700523 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800524 if (DEBUG) {
525 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
526 + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
527 }
528 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
529 }
530
531 private void updateAutoBrightness(boolean sendUpdate) {
532 if (!mAmbientLuxValid) {
533 return;
534 }
535
536 float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
537 float gamma = 1.0f;
538
539 if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
540 && mScreenAutoBrightnessAdjustment != 0.0f) {
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100541 final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma,
Michael Wright639c8be2014-01-17 18:29:12 -0800542 Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
543 gamma *= adjGamma;
544 if (DEBUG) {
545 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
546 }
547 }
548
Michael Wright639c8be2014-01-17 18:29:12 -0800549 if (gamma != 1.0f) {
550 final float in = value;
551 value = MathUtils.pow(value, gamma);
552 if (DEBUG) {
553 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
554 + ", in=" + in + ", out=" + value);
555 }
556 }
557
558 int newScreenAutoBrightness =
Jeff Browna576b4d2015-04-23 19:58:06 -0700559 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
Michael Wright639c8be2014-01-17 18:29:12 -0800560 if (mScreenAutoBrightness != newScreenAutoBrightness) {
561 if (DEBUG) {
562 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
563 + mScreenAutoBrightness + ", newScreenAutoBrightness="
564 + newScreenAutoBrightness);
565 }
566
567 mScreenAutoBrightness = newScreenAutoBrightness;
568 mLastScreenAutoBrightnessGamma = gamma;
569 if (sendUpdate) {
570 mCallbacks.updateBrightness();
571 }
572 }
573 }
574
575 private int clampScreenBrightness(int value) {
576 return MathUtils.constrain(value,
577 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
578 }
579
Jeff Browna576b4d2015-04-23 19:58:06 -0700580 private void prepareBrightnessAdjustmentSample() {
581 if (!mBrightnessAdjustmentSamplePending) {
582 mBrightnessAdjustmentSamplePending = true;
583 mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment;
584 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
585 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
586 mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma;
587 } else {
588 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
589 }
590
591 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
592 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
593 }
594
595 private void cancelBrightnessAdjustmentSample() {
596 if (mBrightnessAdjustmentSamplePending) {
597 mBrightnessAdjustmentSamplePending = false;
598 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
599 }
600 }
601
602 private void collectBrightnessAdjustmentSample() {
603 if (mBrightnessAdjustmentSamplePending) {
604 mBrightnessAdjustmentSamplePending = false;
605 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
606 if (DEBUG) {
607 Slog.d(TAG, "Auto-brightness adjustment changed by user: "
608 + "adj=" + mScreenAutoBrightnessAdjustment
609 + ", lux=" + mAmbientLux
610 + ", brightness=" + mScreenAutoBrightness
611 + ", gamma=" + mLastScreenAutoBrightnessGamma
612 + ", ring=" + mAmbientLightRingBuffer);
613 }
614
615 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
616 mBrightnessAdjustmentSampleOldAdjustment,
617 mBrightnessAdjustmentSampleOldLux,
618 mBrightnessAdjustmentSampleOldBrightness,
619 mBrightnessAdjustmentSampleOldGamma,
620 mScreenAutoBrightnessAdjustment,
621 mAmbientLux,
622 mScreenAutoBrightness,
623 mLastScreenAutoBrightnessGamma);
624 }
625 }
626 }
627
Michael Wright639c8be2014-01-17 18:29:12 -0800628 private final class AutomaticBrightnessHandler extends Handler {
629 public AutomaticBrightnessHandler(Looper looper) {
630 super(looper, null, true /*async*/);
631 }
632
633 @Override
634 public void handleMessage(Message msg) {
635 switch (msg.what) {
636 case MSG_UPDATE_AMBIENT_LUX:
637 updateAmbientLux();
638 break;
Jeff Browna576b4d2015-04-23 19:58:06 -0700639
640 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
641 collectBrightnessAdjustmentSample();
642 break;
Michael Wright639c8be2014-01-17 18:29:12 -0800643 }
644 }
645 }
646
647 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
648 @Override
649 public void onSensorChanged(SensorEvent event) {
650 if (mLightSensorEnabled) {
651 final long time = SystemClock.uptimeMillis();
652 final float lux = event.values[0];
653 handleLightSensorEvent(time, lux);
654 }
655 }
656
657 @Override
658 public void onAccuracyChanged(Sensor sensor, int accuracy) {
659 // Not used.
660 }
661 };
662
Michael Wright639c8be2014-01-17 18:29:12 -0800663 /** Callbacks to request updates to the display's power state. */
664 interface Callbacks {
665 void updateBrightness();
666 }
667
Michael Wright0a933142017-08-16 20:38:21 +0100668 /**
669 * A ring buffer of ambient light measurements sorted by time.
670 *
671 * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
672 * from oldest to newest.
673 */
Michael Wright103fb782016-04-22 01:03:09 -0400674 private static final class AmbientLightRingBuffer {
Michael Wright639c8be2014-01-17 18:29:12 -0800675 // Proportional extra capacity of the buffer beyond the expected number of light samples
676 // in the horizon
677 private static final float BUFFER_SLACK = 1.5f;
Michael Wright639c8be2014-01-17 18:29:12 -0800678 private float[] mRingLux;
679 private long[] mRingTime;
680 private int mCapacity;
681
682 // The first valid element and the next open slot.
683 // Note that if mCount is zero then there are no valid elements.
684 private int mStart;
685 private int mEnd;
686 private int mCount;
687
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100688 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
689 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800690 mRingLux = new float[mCapacity];
691 mRingTime = new long[mCapacity];
692 }
693
694 public float getLux(int index) {
695 return mRingLux[offsetOf(index)];
696 }
697
698 public long getTime(int index) {
699 return mRingTime[offsetOf(index)];
700 }
701
702 public void push(long time, float lux) {
703 int next = mEnd;
704 if (mCount == mCapacity) {
705 int newSize = mCapacity * 2;
706
707 float[] newRingLux = new float[newSize];
708 long[] newRingTime = new long[newSize];
709 int length = mCapacity - mStart;
710 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
711 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
712 if (mStart != 0) {
713 System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
714 System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
715 }
716 mRingLux = newRingLux;
717 mRingTime = newRingTime;
718
719 next = mCapacity;
720 mCapacity = newSize;
721 mStart = 0;
722 }
723 mRingTime[next] = time;
724 mRingLux[next] = lux;
725 mEnd = next + 1;
726 if (mEnd == mCapacity) {
727 mEnd = 0;
728 }
729 mCount++;
730 }
731
732 public void prune(long horizon) {
733 if (mCount == 0) {
734 return;
735 }
736
737 while (mCount > 1) {
738 int next = mStart + 1;
739 if (next >= mCapacity) {
740 next -= mCapacity;
741 }
742 if (mRingTime[next] > horizon) {
743 // Some light sensors only produce data upon a change in the ambient light
744 // levels, so we need to consider the previous measurement as the ambient light
745 // level for all points in time up until we receive a new measurement. Thus, we
746 // always want to keep the youngest element that would be removed from the
747 // buffer and just set its measurement time to the horizon time since at that
748 // point it is the ambient light level, and to remove it would be to drop a
749 // valid data point within our horizon.
750 break;
751 }
752 mStart = next;
753 mCount -= 1;
754 }
755
756 if (mRingTime[mStart] < horizon) {
757 mRingTime[mStart] = horizon;
758 }
759 }
760
761 public int size() {
762 return mCount;
763 }
764
Michael Wright639c8be2014-01-17 18:29:12 -0800765 public void clear() {
766 mStart = 0;
767 mEnd = 0;
768 mCount = 0;
769 }
770
771 @Override
772 public String toString() {
Jeff Browna576b4d2015-04-23 19:58:06 -0700773 StringBuffer buf = new StringBuffer();
774 buf.append('[');
775 for (int i = 0; i < mCount; i++) {
776 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
777 if (i != 0) {
778 buf.append(", ");
779 }
780 buf.append(getLux(i));
781 buf.append(" / ");
782 buf.append(next - getTime(i));
783 buf.append("ms");
Michael Wright639c8be2014-01-17 18:29:12 -0800784 }
Jeff Browna576b4d2015-04-23 19:58:06 -0700785 buf.append(']');
786 return buf.toString();
Michael Wright639c8be2014-01-17 18:29:12 -0800787 }
788
789 private int offsetOf(int index) {
790 if (index >= mCount || index < 0) {
791 throw new ArrayIndexOutOfBoundsException(index);
792 }
793 index += mStart;
794 if (index >= mCapacity) {
795 index -= mCapacity;
796 }
797 return index;
798 }
799 }
800}