blob: 2612b53e1052bb6b83eb6f8c1098f22044a8c804 [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
Justin Klaassen908b86c2016-08-08 09:18:42 -070019import android.annotation.Nullable;
Michael Wright639c8be2014-01-17 18:29:12 -080020import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
Michael Wrighteef0e132017-11-21 17:57:52 +000024import android.hardware.display.BrightnessConfiguration;
Michael Wrightd8460232018-01-16 18:04:59 +000025import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
Michael Wright639c8be2014-01-17 18:29:12 -080026import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.os.PowerManager;
30import android.os.SystemClock;
Michael Wrightb0a1d3d2017-09-22 15:05:02 +010031import android.os.Trace;
Jeff Browna576b4d2015-04-23 19:58:06 -070032import android.util.EventLog;
Michael Wright639c8be2014-01-17 18:29:12 -080033import android.util.MathUtils;
Michael Wright639c8be2014-01-17 18:29:12 -080034import android.util.Slog;
35import android.util.TimeUtils;
36
Dan Gittikfe87cf32019-01-30 17:31:34 +000037import com.android.server.EventLogTags;
38
Michael Wright639c8be2014-01-17 18:29:12 -080039import 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 Wrightd8460232018-01-16 18:04:59 +000059 // Timeout after which we remove the effects any user interactions might've had on the
60 // brightness mapping. This timeout doesn't start until we transition to a non-interactive
61 // display policy so that we don't reset while users are using their devices, but also so that
62 // we don't erroneously keep the short-term model if the device is dozing but the display is
63 // fully on.
64 private static final int SHORT_TERM_MODEL_TIMEOUT_MILLIS = 30000;
65
Michael Wright639c8be2014-01-17 18:29:12 -080066 private static final int MSG_UPDATE_AMBIENT_LUX = 1;
Jeff Browna576b4d2015-04-23 19:58:06 -070067 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
Dan Gittikc5d32912018-02-22 13:53:47 +000068 private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
Michael Wright639c8be2014-01-17 18:29:12 -080069
Michael Wright0a933142017-08-16 20:38:21 +010070 // Length of the ambient light horizon used to calculate the long term estimate of ambient
71 // light.
72 private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
73
74 // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
75 private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
76
Julius D'souza428aed02016-08-07 19:08:30 -070077 // Callbacks for requesting updates to the display's power state
Michael Wright639c8be2014-01-17 18:29:12 -080078 private final Callbacks mCallbacks;
79
80 // The sensor manager.
81 private final SensorManager mSensorManager;
82
83 // The light sensor, or null if not available or needed.
84 private final Sensor mLightSensor;
85
Michael Wrighteef0e132017-11-21 17:57:52 +000086 // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
87 private final BrightnessMappingStrategy mBrightnessMapper;
Michael Wright639c8be2014-01-17 18:29:12 -080088
89 // The minimum and maximum screen brightnesses.
90 private final int mScreenBrightnessRangeMinimum;
91 private final int mScreenBrightnessRangeMaximum;
Michael Wright2155bc22018-05-01 00:38:32 +010092
93 // How much to scale doze brightness by (should be (0, 1.0]).
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -070094 private final float mDozeScaleFactor;
Michael Wright639c8be2014-01-17 18:29:12 -080095
Julius D'souza5d717092016-10-24 19:26:45 -070096 // 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;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800104
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
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100118 // 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
Michael Wright2155bc22018-05-01 00:38:32 +0100125 // Configuration object for determining thresholds to change brightness dynamically
Dan Gittikfe87cf32019-01-30 17:31:34 +0000126 private final HysteresisLevels mAmbientBrightnessThresholds;
127 private final HysteresisLevels mScreenBrightnessThresholds;
Julius D'souza428aed02016-08-07 19:08:30 -0700128
Michael Wright639c8be2014-01-17 18:29:12 -0800129 // Amount of time to delay auto-brightness after screen on while waiting for
130 // the light sensor to warm-up in milliseconds.
131 // May be 0 if no warm-up is required.
132 private int mLightSensorWarmUpTimeConfig;
133
134 // Set to true if the light sensor is enabled.
135 private boolean mLightSensorEnabled;
136
137 // The time when the light sensor was enabled.
138 private long mLightSensorEnableTime;
139
140 // The currently accepted nominal ambient light level.
141 private float mAmbientLux;
142
143 // True if mAmbientLux holds a valid value.
144 private boolean mAmbientLuxValid;
145
146 // The ambient light level threshold at which to brighten or darken the screen.
Dan Gittikfe87cf32019-01-30 17:31:34 +0000147 private float mAmbientBrighteningThreshold;
148 private float mAmbientDarkeningThreshold;
149
150 // The screen light level threshold at which to brighten or darken the screen.
151 private float mScreenBrighteningThreshold;
152 private float mScreenDarkeningThreshold;
Michael Wright639c8be2014-01-17 18:29:12 -0800153
154 // The most recent light sample.
155 private float mLastObservedLux;
156
157 // The time of the most light recent sample.
158 private long mLastObservedLuxTime;
159
160 // The number of light samples collected since the light sensor was enabled.
161 private int mRecentLightSamples;
162
163 // A ring buffer containing all of the recent ambient light sensor readings.
164 private AmbientLightRingBuffer mAmbientLightRingBuffer;
165
166 // The handler
167 private AutomaticBrightnessHandler mHandler;
168
169 // The screen brightness level that has been chosen by the auto-brightness
170 // algorithm. The actual brightness should ramp towards this value.
171 // We preserve this value even when we stop using the light sensor so
172 // that we can quickly revert to the previous auto-brightness level
173 // while the light sensor warms up.
174 // Use -1 if there is no current auto-brightness value available.
175 private int mScreenAutoBrightness = -1;
176
Michael Wrightd8460232018-01-16 18:04:59 +0000177 // The current display policy. This is useful, for example, for knowing when we're dozing,
178 // where the light sensor may not be available.
179 private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700180
Jeff Browna576b4d2015-04-23 19:58:06 -0700181 // True if we are collecting a brightness adjustment sample, along with some data
182 // for the initial state of the sample.
183 private boolean mBrightnessAdjustmentSamplePending;
Jeff Browna576b4d2015-04-23 19:58:06 -0700184 private float mBrightnessAdjustmentSampleOldLux;
185 private int mBrightnessAdjustmentSampleOldBrightness;
Jeff Browna576b4d2015-04-23 19:58:06 -0700186
Dan Gittikc5d32912018-02-22 13:53:47 +0000187 // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
188 // user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
189 // The anchor determines what were the light levels when the user has set her preference, and
190 // we use a relative threshold to determine when to revert to the OEM curve.
191 private boolean mShortTermModelValid;
192 private float mShortTermModelAnchor;
193 private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
194
Michael Wright639c8be2014-01-17 18:29:12 -0800195 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
Dan Gittik57d6f112018-03-27 18:14:22 +0100196 SensorManager sensorManager, BrightnessMappingStrategy mapper,
197 int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
Julius D'souza5d717092016-10-24 19:26:45 -0700198 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100199 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
Dan Gittikfe87cf32019-01-30 17:31:34 +0000200 HysteresisLevels ambientBrightnessThresholds,
201 HysteresisLevels screenBrightnessThresholds) {
Michael Wright639c8be2014-01-17 18:29:12 -0800202 mCallbacks = callbacks;
Michael Wright639c8be2014-01-17 18:29:12 -0800203 mSensorManager = sensorManager;
Michael Wrighteef0e132017-11-21 17:57:52 +0000204 mBrightnessMapper = mapper;
Michael Wright639c8be2014-01-17 18:29:12 -0800205 mScreenBrightnessRangeMinimum = brightnessMin;
206 mScreenBrightnessRangeMaximum = brightnessMax;
207 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700208 mDozeScaleFactor = dozeScaleFactor;
Julius D'souza5d717092016-10-24 19:26:45 -0700209 mNormalLightSensorRate = lightSensorRate;
210 mInitialLightSensorRate = initialLightSensorRate;
211 mCurrentLightSensorRate = -1;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800212 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
213 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
214 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
Michael Wright2155bc22018-05-01 00:38:32 +0100215 mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
216 mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
Dan Gittikfe87cf32019-01-30 17:31:34 +0000217 mAmbientBrightnessThresholds = ambientBrightnessThresholds;
218 mScreenBrightnessThresholds = screenBrightnessThresholds;
Dan Gittikc5d32912018-02-22 13:53:47 +0000219 mShortTermModelValid = true;
220 mShortTermModelAnchor = -1;
Michael Wright639c8be2014-01-17 18:29:12 -0800221
222 mHandler = new AutomaticBrightnessHandler(looper);
Michael Wright103fb782016-04-22 01:03:09 -0400223 mAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700224 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800225
226 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
227 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
228 }
Michael Wright639c8be2014-01-17 18:29:12 -0800229 }
230
231 public int getAutomaticScreenBrightness() {
Dan Gittik58784422018-04-05 12:30:38 +0100232 if (!mAmbientLuxValid) {
233 return -1;
234 }
Michael Wrightd8460232018-01-16 18:04:59 +0000235 if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700236 return (int) (mScreenAutoBrightness * mDozeScaleFactor);
237 }
Michael Wright639c8be2014-01-17 18:29:12 -0800238 return mScreenAutoBrightness;
239 }
240
Michael Wright617564f2018-01-25 22:20:54 +0000241 public float getAutomaticScreenBrightnessAdjustment() {
Dan Gittik57d6f112018-03-27 18:14:22 +0100242 return mBrightnessMapper.getAutoBrightnessAdjustment();
Michael Wright617564f2018-01-25 22:20:54 +0000243 }
244
Michael Wrighteef0e132017-11-21 17:57:52 +0000245 public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
Michael Wright617564f2018-01-25 22:20:54 +0000246 float brightness, boolean userChangedBrightness, float adjustment,
247 boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700248 // While dozing, the application processor may be suspended which will prevent us from
249 // receiving new information from the light sensor. On some devices, we may be able to
250 // switch to a wake-up light sensor instead but for now we will simply disable the sensor
251 // and hold onto the last computed screen auto brightness. We save the dozing flag for
252 // debugging purposes.
Michael Wrightd8460232018-01-16 18:04:59 +0000253 boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
Michael Wrighteef0e132017-11-21 17:57:52 +0000254 boolean changed = setBrightnessConfiguration(configuration);
Michael Wrightd8460232018-01-16 18:04:59 +0000255 changed |= setDisplayPolicy(displayPolicy);
Dan Gittik57d6f112018-03-27 18:14:22 +0100256 if (userChangedAutoBrightnessAdjustment) {
257 changed |= setAutoBrightnessAdjustment(adjustment);
258 }
Michael Wright617564f2018-01-25 22:20:54 +0000259 if (userChangedBrightness && enable) {
260 // Update the brightness curve with the new user control point. It's critical this
261 // happens after we update the autobrightness adjustment since it may reset it.
Michael Wrightd8460232018-01-16 18:04:59 +0000262 changed |= setScreenBrightnessByUser(brightness);
Michael Wright617564f2018-01-25 22:20:54 +0000263 }
264 final boolean userInitiatedChange =
265 userChangedBrightness || userChangedAutoBrightnessAdjustment;
266 if (userInitiatedChange && enable && !dozing) {
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -0700267 prepareBrightnessAdjustmentSample();
268 }
Michael Wrightd8460232018-01-16 18:04:59 +0000269 changed |= setLightSensorEnabled(enable && !dozing);
Jeff Brown970d4132014-07-19 11:33:47 -0700270 if (changed) {
Michael Wright639c8be2014-01-17 18:29:12 -0800271 updateAutoBrightness(false /*sendUpdate*/);
272 }
273 }
274
Kenny Guy53d06612018-01-30 14:19:13 +0000275 public boolean hasUserDataPoints() {
276 return mBrightnessMapper.hasUserDataPoints();
277 }
278
279 public boolean isDefaultConfig() {
280 return mBrightnessMapper.isDefaultConfig();
281 }
282
Kenny Guy6d1009f2018-03-14 14:28:23 +0000283 public BrightnessConfiguration getDefaultConfig() {
284 return mBrightnessMapper.getDefaultConfig();
285 }
286
Michael Wrightd8460232018-01-16 18:04:59 +0000287 private boolean setDisplayPolicy(int policy) {
288 if (mDisplayPolicy == policy) {
289 return false;
290 }
291 final int oldPolicy = mDisplayPolicy;
292 mDisplayPolicy = policy;
293 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000294 Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
Michael Wrightd8460232018-01-16 18:04:59 +0000295 }
296 if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000297 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
Michael Wrightd8460232018-01-16 18:04:59 +0000298 SHORT_TERM_MODEL_TIMEOUT_MILLIS);
299 } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000300 mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
Michael Wrightd8460232018-01-16 18:04:59 +0000301 }
302 return true;
303 }
304
305 private static boolean isInteractivePolicy(int policy) {
306 return policy == DisplayPowerRequest.POLICY_BRIGHT
307 || policy == DisplayPowerRequest.POLICY_DIM
308 || policy == DisplayPowerRequest.POLICY_VR;
309 }
310
311 private boolean setScreenBrightnessByUser(float brightness) {
312 if (!mAmbientLuxValid) {
313 // If we don't have a valid ambient lux then we don't have a valid brightness anyways,
314 // and we can't use this data to add a new control point to the short-term model.
315 return false;
316 }
317 mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
Dan Gittikc5d32912018-02-22 13:53:47 +0000318 mShortTermModelValid = true;
319 mShortTermModelAnchor = mAmbientLux;
320 if (DEBUG) {
321 Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
322 }
Michael Wrightd8460232018-01-16 18:04:59 +0000323 return true;
324 }
325
Dan Gittikc1352252018-04-27 17:48:31 +0100326 public void resetShortTermModel() {
Michael Wrightd8460232018-01-16 18:04:59 +0000327 mBrightnessMapper.clearUserDataPoints();
Dan Gittikc5d32912018-02-22 13:53:47 +0000328 mShortTermModelValid = true;
329 mShortTermModelAnchor = -1;
330 }
331
332 private void invalidateShortTermModel() {
333 if (DEBUG) {
334 Slog.d(TAG, "ShortTermModel: invalidate user data");
335 }
336 mShortTermModelValid = false;
Michael Wrightd8460232018-01-16 18:04:59 +0000337 }
338
Michael Wrighteef0e132017-11-21 17:57:52 +0000339 public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
Michael Wrightb8f4f772018-05-01 18:28:58 +0100340 if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
341 resetShortTermModel();
342 return true;
343 }
344 return false;
Michael Wrighteef0e132017-11-21 17:57:52 +0000345 }
346
Michael Wright639c8be2014-01-17 18:29:12 -0800347 public void dump(PrintWriter pw) {
348 pw.println();
349 pw.println("Automatic Brightness Controller Configuration:");
Michael Wright639c8be2014-01-17 18:29:12 -0800350 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
351 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
Michael Wright2155bc22018-05-01 00:38:32 +0100352 pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
353 pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
354 pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800355 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800356 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
357 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
358 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
Michael Wright2155bc22018-05-01 00:38:32 +0100359 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
360 pw.println(" mWeightingIntercept=" + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800361
362 pw.println();
363 pw.println("Automatic Brightness Controller State:");
364 pw.println(" mLightSensor=" + mLightSensor);
Michael Wright639c8be2014-01-17 18:29:12 -0800365 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
366 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
Michael Wright2155bc22018-05-01 00:38:32 +0100367 pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800368 pw.println(" mAmbientLux=" + mAmbientLux);
Dan Gittik58784422018-04-05 12:30:38 +0100369 pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
Dan Gittikfe87cf32019-01-30 17:31:34 +0000370 pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
371 pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
372 pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
373 pw.println(" mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
Michael Wright639c8be2014-01-17 18:29:12 -0800374 pw.println(" mLastObservedLux=" + mLastObservedLux);
375 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
376 pw.println(" mRecentLightSamples=" + mRecentLightSamples);
377 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
378 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
Michael Wright2155bc22018-05-01 00:38:32 +0100379 pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
Dan Gittikc5d32912018-02-22 13:53:47 +0000380 pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
Michael Wright2155bc22018-05-01 00:38:32 +0100381 pw.println(" mShortTermModelValid=" + mShortTermModelValid);
382 pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
383 pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
384 pw.println(" mBrightnessAdjustmentSampleOldBrightness="
385 + mBrightnessAdjustmentSampleOldBrightness);
386 pw.println(" mShortTermModelValid=" + mShortTermModelValid);
Michael Wrighteef0e132017-11-21 17:57:52 +0000387
388 pw.println();
389 mBrightnessMapper.dump(pw);
Michael Wright2155bc22018-05-01 00:38:32 +0100390
391 pw.println();
Dan Gittikfe87cf32019-01-30 17:31:34 +0000392 mAmbientBrightnessThresholds.dump(pw);
393 mScreenBrightnessThresholds.dump(pw);
Michael Wright639c8be2014-01-17 18:29:12 -0800394 }
395
396 private boolean setLightSensorEnabled(boolean enable) {
397 if (enable) {
398 if (!mLightSensorEnabled) {
399 mLightSensorEnabled = true;
400 mLightSensorEnableTime = SystemClock.uptimeMillis();
Julius D'souza5d717092016-10-24 19:26:45 -0700401 mCurrentLightSensorRate = mInitialLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800402 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
Julius D'souza5d717092016-10-24 19:26:45 -0700403 mCurrentLightSensorRate * 1000, mHandler);
Michael Wright639c8be2014-01-17 18:29:12 -0800404 return true;
405 }
Dan Gittikc5d32912018-02-22 13:53:47 +0000406 } else if (mLightSensorEnabled) {
407 mLightSensorEnabled = false;
408 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
409 mRecentLightSamples = 0;
410 mAmbientLightRingBuffer.clear();
411 mCurrentLightSensorRate = -1;
412 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
413 mSensorManager.unregisterListener(mLightSensorListener);
Michael Wright639c8be2014-01-17 18:29:12 -0800414 }
415 return false;
416 }
417
418 private void handleLightSensorEvent(long time, float lux) {
Michael Wrightb0a1d3d2017-09-22 15:05:02 +0100419 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
Michael Wright639c8be2014-01-17 18:29:12 -0800420 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
421
Julius D'souza5d717092016-10-24 19:26:45 -0700422 if (mAmbientLightRingBuffer.size() == 0) {
423 // switch to using the steady-state sample rate after grabbing the initial light sample
424 adjustLightSensorRate(mNormalLightSensorRate);
425 }
Michael Wright639c8be2014-01-17 18:29:12 -0800426 applyLightSensorMeasurement(time, lux);
427 updateAmbientLux(time);
428 }
429
430 private void applyLightSensorMeasurement(long time, float lux) {
431 mRecentLightSamples++;
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100432 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800433 mAmbientLightRingBuffer.push(time, lux);
434
435 // Remember this sample value.
436 mLastObservedLux = lux;
437 mLastObservedLuxTime = time;
438 }
439
Julius D'souza5d717092016-10-24 19:26:45 -0700440 private void adjustLightSensorRate(int lightSensorRate) {
441 // if the light sensor rate changed, update the sensor listener
442 if (lightSensorRate != mCurrentLightSensorRate) {
443 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000444 Slog.d(TAG, "adjustLightSensorRate: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100445 "previousRate=" + mCurrentLightSensorRate + ", " +
446 "currentRate=" + lightSensorRate);
Julius D'souza5d717092016-10-24 19:26:45 -0700447 }
448 mCurrentLightSensorRate = lightSensorRate;
449 mSensorManager.unregisterListener(mLightSensorListener);
450 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
451 lightSensorRate * 1000, mHandler);
452 }
453 }
454
Dan Gittik57d6f112018-03-27 18:14:22 +0100455 private boolean setAutoBrightnessAdjustment(float adjustment) {
456 return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
Michael Wright639c8be2014-01-17 18:29:12 -0800457 }
458
459 private void setAmbientLux(float lux) {
Michael Wright0a933142017-08-16 20:38:21 +0100460 if (DEBUG) {
461 Slog.d(TAG, "setAmbientLux(" + lux + ")");
462 }
Michael Wrightd8460232018-01-16 18:04:59 +0000463 if (lux < 0) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000464 Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
Michael Wrightd8460232018-01-16 18:04:59 +0000465 lux = 0;
466 }
Michael Wright639c8be2014-01-17 18:29:12 -0800467 mAmbientLux = lux;
Dan Gittikfe87cf32019-01-30 17:31:34 +0000468 mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
469 mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
Dan Gittikc5d32912018-02-22 13:53:47 +0000470
471 // If the short term model was invalidated and the change is drastic enough, reset it.
472 if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
473 final float minAmbientLux =
474 mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
475 final float maxAmbientLux =
476 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
477 if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
Dan Gittik57d6f112018-03-27 18:14:22 +0100478 if (DEBUG) {
479 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
480 minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
481 }
Dan Gittikc5d32912018-02-22 13:53:47 +0000482 mShortTermModelValid = true;
483 } else {
484 Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + mAmbientLux +
Dan Gittik57d6f112018-03-27 18:14:22 +0100485 "(" + minAmbientLux + ", " + maxAmbientLux + ")");
Dan Gittikc5d32912018-02-22 13:53:47 +0000486 resetShortTermModel();
487 }
488 }
Michael Wright639c8be2014-01-17 18:29:12 -0800489 }
490
Michael Wright0a933142017-08-16 20:38:21 +0100491 private float calculateAmbientLux(long now, long horizon) {
492 if (DEBUG) {
493 Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
494 }
Michael Wright639c8be2014-01-17 18:29:12 -0800495 final int N = mAmbientLightRingBuffer.size();
496 if (N == 0) {
497 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
498 return -1;
499 }
Michael Wright0a933142017-08-16 20:38:21 +0100500
501 // Find the first measurement that is just outside of the horizon.
502 int endIndex = 0;
503 final long horizonStartTime = now - horizon;
504 for (int i = 0; i < N-1; i++) {
505 if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
506 endIndex++;
507 } else {
508 break;
509 }
510 }
511 if (DEBUG) {
Dan Gittik57d6f112018-03-27 18:14:22 +0100512 Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
513 mAmbientLightRingBuffer.getTime(endIndex) + ", " +
514 mAmbientLightRingBuffer.getLux(endIndex) + ")");
Michael Wright0a933142017-08-16 20:38:21 +0100515 }
Michael Wright639c8be2014-01-17 18:29:12 -0800516 float sum = 0;
517 float totalWeight = 0;
518 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
Michael Wright0a933142017-08-16 20:38:21 +0100519 for (int i = N - 1; i >= endIndex; i--) {
520 long eventTime = mAmbientLightRingBuffer.getTime(i);
521 if (i == endIndex && eventTime < horizonStartTime) {
522 // If we're at the final value, make sure we only consider the part of the sample
523 // within our desired horizon.
524 eventTime = horizonStartTime;
525 }
526 final long startTime = eventTime - now;
Michael Wright639c8be2014-01-17 18:29:12 -0800527 float weight = calculateWeight(startTime, endTime);
528 float lux = mAmbientLightRingBuffer.getLux(i);
529 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000530 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100531 "lux=" + lux + ", " +
532 "weight=" + weight);
Michael Wright639c8be2014-01-17 18:29:12 -0800533 }
534 totalWeight += weight;
Dan Gittikc5d32912018-02-22 13:53:47 +0000535 sum += lux * weight;
Michael Wright639c8be2014-01-17 18:29:12 -0800536 endTime = startTime;
537 }
538 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000539 Slog.d(TAG, "calculateAmbientLux: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100540 "totalWeight=" + totalWeight + ", " +
541 "newAmbientLux=" + (sum / totalWeight));
Michael Wright639c8be2014-01-17 18:29:12 -0800542 }
543 return sum / totalWeight;
544 }
545
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100546 private float calculateWeight(long startDelta, long endDelta) {
Michael Wright639c8be2014-01-17 18:29:12 -0800547 return weightIntegral(endDelta) - weightIntegral(startDelta);
548 }
549
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100550 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
Michael Wright639c8be2014-01-17 18:29:12 -0800551 // horizon we're looking at and provides a non-linear weighting for light samples.
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100552 private float weightIntegral(long x) {
553 return x * (x * 0.5f + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800554 }
555
556 private long nextAmbientLightBrighteningTransition(long time) {
557 final int N = mAmbientLightRingBuffer.size();
558 long earliestValidTime = time;
559 for (int i = N - 1; i >= 0; i--) {
Dan Gittikfe87cf32019-01-30 17:31:34 +0000560 if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
Michael Wright639c8be2014-01-17 18:29:12 -0800561 break;
562 }
563 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
564 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800565 return earliestValidTime + mBrighteningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800566 }
567
568 private long nextAmbientLightDarkeningTransition(long time) {
569 final int N = mAmbientLightRingBuffer.size();
570 long earliestValidTime = time;
571 for (int i = N - 1; i >= 0; i--) {
Dan Gittikfe87cf32019-01-30 17:31:34 +0000572 if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
Michael Wright639c8be2014-01-17 18:29:12 -0800573 break;
574 }
575 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
576 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800577 return earliestValidTime + mDarkeningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800578 }
579
580 private void updateAmbientLux() {
581 long time = SystemClock.uptimeMillis();
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100582 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800583 updateAmbientLux(time);
584 }
585
586 private void updateAmbientLux(long time) {
587 // If the light sensor was just turned on then immediately update our initial
588 // estimate of the current ambient light level.
589 if (!mAmbientLuxValid) {
590 final long timeWhenSensorWarmedUp =
591 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
592 if (time < timeWhenSensorWarmedUp) {
593 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000594 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100595 "time=" + time + ", " +
596 "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
Michael Wright639c8be2014-01-17 18:29:12 -0800597 }
598 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
599 timeWhenSensorWarmedUp);
600 return;
601 }
Michael Wright0a933142017-08-16 20:38:21 +0100602 setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
Michael Wright639c8be2014-01-17 18:29:12 -0800603 mAmbientLuxValid = true;
604 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000605 Slog.d(TAG, "updateAmbientLux: Initializing: " +
606 "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
607 "mAmbientLux=" + mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800608 }
609 updateAutoBrightness(true);
610 }
611
612 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
613 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
Michael Wright0a933142017-08-16 20:38:21 +0100614 // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
615 // change in lighting conditions, and a fast ambient lux to determine what the new
616 // brightness situation is since the slow lux can be quite slow to converge.
617 //
618 // Note that both values need to be checked for sufficient change before updating the
619 // proposed ambient light value since the slow value might be sufficiently far enough away
620 // from the fast value to cause a recalculation while its actually just converging on
621 // the fast value still.
622 float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
623 float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
Michael Wright639c8be2014-01-17 18:29:12 -0800624
Dan Gittikfe87cf32019-01-30 17:31:34 +0000625 if ((slowAmbientLux >= mAmbientBrighteningThreshold
626 && fastAmbientLux >= mAmbientBrighteningThreshold
627 && nextBrightenTransition <= time)
628 || (slowAmbientLux <= mAmbientDarkeningThreshold
629 && fastAmbientLux <= mAmbientDarkeningThreshold
630 && nextDarkenTransition <= time)) {
Michael Wright0a933142017-08-16 20:38:21 +0100631 setAmbientLux(fastAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800632 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000633 Slog.d(TAG, "updateAmbientLux: " +
Dan Gittikfe87cf32019-01-30 17:31:34 +0000634 ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
635 + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
636 + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
637 + "mAmbientLux=" + mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800638 }
639 updateAutoBrightness(true);
640 nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
641 nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
642 }
643 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
644 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
645 // exceed the necessary threshold, then it's possible we'll get a transition time prior to
646 // now. Rather than continually checking to see whether the weighted lux exceeds the
647 // threshold, schedule an update for when we'd normally expect another light sample, which
648 // should be enough time to decide whether we should actually transition to the new
649 // weighted ambient lux or not.
650 nextTransitionTime =
Julius D'souza5d717092016-10-24 19:26:45 -0700651 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800652 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000653 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100654 nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
Michael Wright639c8be2014-01-17 18:29:12 -0800655 }
656 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
657 }
658
659 private void updateAutoBrightness(boolean sendUpdate) {
660 if (!mAmbientLuxValid) {
661 return;
662 }
663
Michael Wrighteef0e132017-11-21 17:57:52 +0000664 float value = mBrightnessMapper.getBrightness(mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800665
666 int newScreenAutoBrightness =
Jeff Browna576b4d2015-04-23 19:58:06 -0700667 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
Dan Gittikfe87cf32019-01-30 17:31:34 +0000668
669 // If mScreenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
670 // in which case we ignore the new screen brightness if it doesn't differ enough from the
671 // previous one.
672 if (mScreenAutoBrightness != -1
673 && newScreenAutoBrightness > mScreenDarkeningThreshold
674 && newScreenAutoBrightness < mScreenBrighteningThreshold) {
675 if (DEBUG) {
676 Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
677 + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
678 }
679 return;
680 }
681
Michael Wright639c8be2014-01-17 18:29:12 -0800682 if (mScreenAutoBrightness != newScreenAutoBrightness) {
683 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000684 Slog.d(TAG, "updateAutoBrightness: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100685 "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
686 "newScreenAutoBrightness=" + newScreenAutoBrightness);
Michael Wright639c8be2014-01-17 18:29:12 -0800687 }
688
689 mScreenAutoBrightness = newScreenAutoBrightness;
Dan Gittikfe87cf32019-01-30 17:31:34 +0000690 mScreenBrighteningThreshold =
691 mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness);
692 mScreenDarkeningThreshold =
693 mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness);
694
Michael Wright639c8be2014-01-17 18:29:12 -0800695 if (sendUpdate) {
696 mCallbacks.updateBrightness();
697 }
698 }
699 }
700
701 private int clampScreenBrightness(int value) {
702 return MathUtils.constrain(value,
703 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
704 }
705
Jeff Browna576b4d2015-04-23 19:58:06 -0700706 private void prepareBrightnessAdjustmentSample() {
707 if (!mBrightnessAdjustmentSamplePending) {
708 mBrightnessAdjustmentSamplePending = true;
Jeff Browna576b4d2015-04-23 19:58:06 -0700709 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
710 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
Jeff Browna576b4d2015-04-23 19:58:06 -0700711 } else {
712 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
713 }
714
715 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
716 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
717 }
718
719 private void cancelBrightnessAdjustmentSample() {
720 if (mBrightnessAdjustmentSamplePending) {
721 mBrightnessAdjustmentSamplePending = false;
722 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
723 }
724 }
725
726 private void collectBrightnessAdjustmentSample() {
727 if (mBrightnessAdjustmentSamplePending) {
728 mBrightnessAdjustmentSamplePending = false;
729 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
730 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000731 Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100732 "lux=" + mAmbientLux + ", " +
733 "brightness=" + mScreenAutoBrightness + ", " +
734 "ring=" + mAmbientLightRingBuffer);
Jeff Browna576b4d2015-04-23 19:58:06 -0700735 }
736
737 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
Jeff Browna576b4d2015-04-23 19:58:06 -0700738 mBrightnessAdjustmentSampleOldLux,
739 mBrightnessAdjustmentSampleOldBrightness,
Jeff Browna576b4d2015-04-23 19:58:06 -0700740 mAmbientLux,
Dan Gittik57d6f112018-03-27 18:14:22 +0100741 mScreenAutoBrightness);
Jeff Browna576b4d2015-04-23 19:58:06 -0700742 }
743 }
744 }
745
Michael Wright639c8be2014-01-17 18:29:12 -0800746 private final class AutomaticBrightnessHandler extends Handler {
747 public AutomaticBrightnessHandler(Looper looper) {
748 super(looper, null, true /*async*/);
749 }
750
751 @Override
752 public void handleMessage(Message msg) {
753 switch (msg.what) {
754 case MSG_UPDATE_AMBIENT_LUX:
755 updateAmbientLux();
756 break;
Jeff Browna576b4d2015-04-23 19:58:06 -0700757
758 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
759 collectBrightnessAdjustmentSample();
760 break;
Michael Wrightd8460232018-01-16 18:04:59 +0000761
Dan Gittikc5d32912018-02-22 13:53:47 +0000762 case MSG_INVALIDATE_SHORT_TERM_MODEL:
763 invalidateShortTermModel();
Michael Wrightd8460232018-01-16 18:04:59 +0000764 break;
Michael Wright639c8be2014-01-17 18:29:12 -0800765 }
766 }
767 }
768
769 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
770 @Override
771 public void onSensorChanged(SensorEvent event) {
772 if (mLightSensorEnabled) {
773 final long time = SystemClock.uptimeMillis();
774 final float lux = event.values[0];
775 handleLightSensorEvent(time, lux);
776 }
777 }
778
779 @Override
780 public void onAccuracyChanged(Sensor sensor, int accuracy) {
781 // Not used.
782 }
783 };
784
Michael Wright639c8be2014-01-17 18:29:12 -0800785 /** Callbacks to request updates to the display's power state. */
786 interface Callbacks {
787 void updateBrightness();
788 }
789
Michael Wright0a933142017-08-16 20:38:21 +0100790 /**
791 * A ring buffer of ambient light measurements sorted by time.
792 *
793 * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
794 * from oldest to newest.
795 */
Michael Wright103fb782016-04-22 01:03:09 -0400796 private static final class AmbientLightRingBuffer {
Michael Wright639c8be2014-01-17 18:29:12 -0800797 // Proportional extra capacity of the buffer beyond the expected number of light samples
798 // in the horizon
799 private static final float BUFFER_SLACK = 1.5f;
Michael Wright639c8be2014-01-17 18:29:12 -0800800 private float[] mRingLux;
801 private long[] mRingTime;
802 private int mCapacity;
803
804 // The first valid element and the next open slot.
805 // Note that if mCount is zero then there are no valid elements.
806 private int mStart;
807 private int mEnd;
808 private int mCount;
809
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100810 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
811 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800812 mRingLux = new float[mCapacity];
813 mRingTime = new long[mCapacity];
814 }
815
816 public float getLux(int index) {
817 return mRingLux[offsetOf(index)];
818 }
819
820 public long getTime(int index) {
821 return mRingTime[offsetOf(index)];
822 }
823
824 public void push(long time, float lux) {
825 int next = mEnd;
826 if (mCount == mCapacity) {
827 int newSize = mCapacity * 2;
828
829 float[] newRingLux = new float[newSize];
830 long[] newRingTime = new long[newSize];
831 int length = mCapacity - mStart;
832 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
833 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
834 if (mStart != 0) {
835 System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
836 System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
837 }
838 mRingLux = newRingLux;
839 mRingTime = newRingTime;
840
841 next = mCapacity;
842 mCapacity = newSize;
843 mStart = 0;
844 }
845 mRingTime[next] = time;
846 mRingLux[next] = lux;
847 mEnd = next + 1;
848 if (mEnd == mCapacity) {
849 mEnd = 0;
850 }
851 mCount++;
852 }
853
854 public void prune(long horizon) {
855 if (mCount == 0) {
856 return;
857 }
858
859 while (mCount > 1) {
860 int next = mStart + 1;
861 if (next >= mCapacity) {
862 next -= mCapacity;
863 }
864 if (mRingTime[next] > horizon) {
865 // Some light sensors only produce data upon a change in the ambient light
866 // levels, so we need to consider the previous measurement as the ambient light
867 // level for all points in time up until we receive a new measurement. Thus, we
868 // always want to keep the youngest element that would be removed from the
869 // buffer and just set its measurement time to the horizon time since at that
870 // point it is the ambient light level, and to remove it would be to drop a
871 // valid data point within our horizon.
872 break;
873 }
874 mStart = next;
875 mCount -= 1;
876 }
877
878 if (mRingTime[mStart] < horizon) {
879 mRingTime[mStart] = horizon;
880 }
881 }
882
883 public int size() {
884 return mCount;
885 }
886
Michael Wright639c8be2014-01-17 18:29:12 -0800887 public void clear() {
888 mStart = 0;
889 mEnd = 0;
890 mCount = 0;
891 }
892
893 @Override
894 public String toString() {
Jeff Browna576b4d2015-04-23 19:58:06 -0700895 StringBuffer buf = new StringBuffer();
896 buf.append('[');
897 for (int i = 0; i < mCount; i++) {
898 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
899 if (i != 0) {
900 buf.append(", ");
901 }
902 buf.append(getLux(i));
903 buf.append(" / ");
904 buf.append(next - getTime(i));
905 buf.append("ms");
Michael Wright639c8be2014-01-17 18:29:12 -0800906 }
Jeff Browna576b4d2015-04-23 19:58:06 -0700907 buf.append(']');
908 return buf.toString();
Michael Wright639c8be2014-01-17 18:29:12 -0800909 }
910
911 private int offsetOf(int index) {
912 if (index >= mCount || index < 0) {
913 throw new ArrayIndexOutOfBoundsException(index);
914 }
915 index += mStart;
916 if (index >= mCapacity) {
917 index -= mCapacity;
918 }
919 return index;
920 }
921 }
922}