blob: 1c3342a9af42d82d0d01755a0d7b66a4159bcd10 [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 Wright2155bc22018-05-01 00:38:32 +010023import android.app.ActivityManager;
Michael Wright639c8be2014-01-17 18:29:12 -080024import android.hardware.Sensor;
25import android.hardware.SensorEvent;
26import android.hardware.SensorEventListener;
27import android.hardware.SensorManager;
Michael Wrighteef0e132017-11-21 17:57:52 +000028import android.hardware.display.BrightnessConfiguration;
Michael Wrightd8460232018-01-16 18:04:59 +000029import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
Michael Wright2155bc22018-05-01 00:38:32 +010030import android.os.Build;
Michael Wright639c8be2014-01-17 18:29:12 -080031import android.os.Handler;
32import android.os.Looper;
33import android.os.Message;
34import android.os.PowerManager;
35import android.os.SystemClock;
Michael Wrightb0a1d3d2017-09-22 15:05:02 +010036import android.os.Trace;
Michael Wright639c8be2014-01-17 18:29:12 -080037import android.text.format.DateUtils;
Jeff Browna576b4d2015-04-23 19:58:06 -070038import android.util.EventLog;
Michael Wright639c8be2014-01-17 18:29:12 -080039import android.util.MathUtils;
Michael Wright639c8be2014-01-17 18:29:12 -080040import android.util.Slog;
41import android.util.TimeUtils;
42
43import java.io.PrintWriter;
Michael Wright639c8be2014-01-17 18:29:12 -080044
45class AutomaticBrightnessController {
46 private static final String TAG = "AutomaticBrightnessController";
47
48 private static final boolean DEBUG = false;
49 private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
50
51 // If true, enables the use of the screen auto-brightness adjustment setting.
Adrian Roosdaf7d412014-05-13 14:55:09 +020052 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
Michael Wright639c8be2014-01-17 18:29:12 -080053
Michael Wright639c8be2014-01-17 18:29:12 -080054 // How long the current sensor reading is assumed to be valid beyond the current time.
55 // This provides a bit of prediction, as well as ensures that the weight for the last sample is
56 // non-zero, which in turn ensures that the total weight is non-zero.
57 private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
58
Jeff Browna576b4d2015-04-23 19:58:06 -070059 // Debounce for sampling user-initiated changes in display brightness to ensure
60 // the user is satisfied with the result before storing the sample.
61 private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
62
Michael Wrightd8460232018-01-16 18:04:59 +000063 // Timeout after which we remove the effects any user interactions might've had on the
64 // brightness mapping. This timeout doesn't start until we transition to a non-interactive
65 // display policy so that we don't reset while users are using their devices, but also so that
66 // we don't erroneously keep the short-term model if the device is dozing but the display is
67 // fully on.
68 private static final int SHORT_TERM_MODEL_TIMEOUT_MILLIS = 30000;
69
Michael Wright639c8be2014-01-17 18:29:12 -080070 private static final int MSG_UPDATE_AMBIENT_LUX = 1;
Jeff Browna576b4d2015-04-23 19:58:06 -070071 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
Dan Gittikc5d32912018-02-22 13:53:47 +000072 private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
Michael Wright639c8be2014-01-17 18:29:12 -080073
Michael Wright0a933142017-08-16 20:38:21 +010074 // Length of the ambient light horizon used to calculate the long term estimate of ambient
75 // light.
76 private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
77
78 // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
79 private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
80
Julius D'souza428aed02016-08-07 19:08:30 -070081 // Callbacks for requesting updates to the display's power state
Michael Wright639c8be2014-01-17 18:29:12 -080082 private final Callbacks mCallbacks;
83
84 // The sensor manager.
85 private final SensorManager mSensorManager;
86
87 // The light sensor, or null if not available or needed.
88 private final Sensor mLightSensor;
89
Michael Wrighteef0e132017-11-21 17:57:52 +000090 // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
91 private final BrightnessMappingStrategy mBrightnessMapper;
Michael Wright639c8be2014-01-17 18:29:12 -080092
93 // The minimum and maximum screen brightnesses.
94 private final int mScreenBrightnessRangeMinimum;
95 private final int mScreenBrightnessRangeMaximum;
Michael Wright2155bc22018-05-01 00:38:32 +010096
97 // How much to scale doze brightness by (should be (0, 1.0]).
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -070098 private final float mDozeScaleFactor;
Michael Wright639c8be2014-01-17 18:29:12 -080099
Julius D'souza5d717092016-10-24 19:26:45 -0700100 // Initial light sensor event rate in milliseconds.
101 private final int mInitialLightSensorRate;
102
103 // Steady-state light sensor event rate in milliseconds.
104 private final int mNormalLightSensorRate;
105
106 // The current light sensor event rate in milliseconds.
107 private int mCurrentLightSensorRate;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800108
109 // Stability requirements in milliseconds for accepting a new brightness level. This is used
110 // for debouncing the light sensor. Different constants are used to debounce the light sensor
111 // when adapting to brighter or darker environments. This parameter controls how quickly
112 // brightness changes occur in response to an observed change in light level that exceeds the
113 // hysteresis threshold.
114 private final long mBrighteningLightDebounceConfig;
115 private final long mDarkeningLightDebounceConfig;
116
117 // If true immediately after the screen is turned on the controller will try to adjust the
118 // brightness based on the current sensor reads. If false, the controller will collect more data
119 // and only then decide whether to change brightness.
120 private final boolean mResetAmbientLuxAfterWarmUpConfig;
121
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100122 // Period of time in which to consider light samples in milliseconds.
123 private final int mAmbientLightHorizon;
124
125 // The intercept used for the weighting calculation. This is used in order to keep all possible
126 // weighting values positive.
127 private final int mWeightingIntercept;
128
Michael Wright2155bc22018-05-01 00:38:32 +0100129 // Configuration object for determining thresholds to change brightness dynamically
130 private final HysteresisLevels mHysteresisLevels;
Julius D'souza428aed02016-08-07 19:08:30 -0700131
Michael Wright639c8be2014-01-17 18:29:12 -0800132 // Amount of time to delay auto-brightness after screen on while waiting for
133 // the light sensor to warm-up in milliseconds.
134 // May be 0 if no warm-up is required.
135 private int mLightSensorWarmUpTimeConfig;
136
137 // Set to true if the light sensor is enabled.
138 private boolean mLightSensorEnabled;
139
140 // The time when the light sensor was enabled.
141 private long mLightSensorEnableTime;
142
143 // The currently accepted nominal ambient light level.
144 private float mAmbientLux;
145
146 // True if mAmbientLux holds a valid value.
147 private boolean mAmbientLuxValid;
148
149 // The ambient light level threshold at which to brighten or darken the screen.
150 private float mBrighteningLuxThreshold;
151 private float mDarkeningLuxThreshold;
152
153 // The most recent light sample.
154 private float mLastObservedLux;
155
156 // The time of the most light recent sample.
157 private long mLastObservedLuxTime;
158
159 // The number of light samples collected since the light sensor was enabled.
160 private int mRecentLightSamples;
161
162 // A ring buffer containing all of the recent ambient light sensor readings.
163 private AmbientLightRingBuffer mAmbientLightRingBuffer;
164
165 // The handler
166 private AutomaticBrightnessHandler mHandler;
167
168 // The screen brightness level that has been chosen by the auto-brightness
169 // algorithm. The actual brightness should ramp towards this value.
170 // We preserve this value even when we stop using the light sensor so
171 // that we can quickly revert to the previous auto-brightness level
172 // while the light sensor warms up.
173 // Use -1 if there is no current auto-brightness value available.
174 private int mScreenAutoBrightness = -1;
175
Michael Wrightd8460232018-01-16 18:04:59 +0000176 // The current display policy. This is useful, for example, for knowing when we're dozing,
177 // where the light sensor may not be available.
178 private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700179
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;
Jeff Browna576b4d2015-04-23 19:58:06 -0700183 private float mBrightnessAdjustmentSampleOldLux;
184 private int mBrightnessAdjustmentSampleOldBrightness;
Jeff Browna576b4d2015-04-23 19:58:06 -0700185
Dan Gittikc5d32912018-02-22 13:53:47 +0000186 // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
187 // user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
188 // The anchor determines what were the light levels when the user has set her preference, and
189 // we use a relative threshold to determine when to revert to the OEM curve.
190 private boolean mShortTermModelValid;
191 private float mShortTermModelAnchor;
192 private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
193
Michael Wright639c8be2014-01-17 18:29:12 -0800194 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
Dan Gittik57d6f112018-03-27 18:14:22 +0100195 SensorManager sensorManager, BrightnessMappingStrategy mapper,
196 int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
Julius D'souza5d717092016-10-24 19:26:45 -0700197 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100198 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
Michael Wright2155bc22018-05-01 00:38:32 +0100199 HysteresisLevels hysteresisLevels) {
Michael Wright639c8be2014-01-17 18:29:12 -0800200 mCallbacks = callbacks;
Michael Wright639c8be2014-01-17 18:29:12 -0800201 mSensorManager = sensorManager;
Michael Wrighteef0e132017-11-21 17:57:52 +0000202 mBrightnessMapper = mapper;
Michael Wright639c8be2014-01-17 18:29:12 -0800203 mScreenBrightnessRangeMinimum = brightnessMin;
204 mScreenBrightnessRangeMaximum = brightnessMax;
205 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700206 mDozeScaleFactor = dozeScaleFactor;
Julius D'souza5d717092016-10-24 19:26:45 -0700207 mNormalLightSensorRate = lightSensorRate;
208 mInitialLightSensorRate = initialLightSensorRate;
209 mCurrentLightSensorRate = -1;
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800210 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
211 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
212 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
Michael Wright2155bc22018-05-01 00:38:32 +0100213 mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
214 mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
215 mHysteresisLevels = hysteresisLevels;
Dan Gittikc5d32912018-02-22 13:53:47 +0000216 mShortTermModelValid = true;
217 mShortTermModelAnchor = -1;
Michael Wright639c8be2014-01-17 18:29:12 -0800218
219 mHandler = new AutomaticBrightnessHandler(looper);
Michael Wright103fb782016-04-22 01:03:09 -0400220 mAmbientLightRingBuffer =
Julius D'souza5d717092016-10-24 19:26:45 -0700221 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800222
223 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
224 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
225 }
Michael Wright639c8be2014-01-17 18:29:12 -0800226 }
227
228 public int getAutomaticScreenBrightness() {
Dan Gittik58784422018-04-05 12:30:38 +0100229 if (!mAmbientLuxValid) {
230 return -1;
231 }
Michael Wrightd8460232018-01-16 18:04:59 +0000232 if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700233 return (int) (mScreenAutoBrightness * mDozeScaleFactor);
234 }
Michael Wright639c8be2014-01-17 18:29:12 -0800235 return mScreenAutoBrightness;
236 }
237
Michael Wright617564f2018-01-25 22:20:54 +0000238 public float getAutomaticScreenBrightnessAdjustment() {
Dan Gittik57d6f112018-03-27 18:14:22 +0100239 return mBrightnessMapper.getAutoBrightnessAdjustment();
Michael Wright617564f2018-01-25 22:20:54 +0000240 }
241
Michael Wrighteef0e132017-11-21 17:57:52 +0000242 public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
Michael Wright617564f2018-01-25 22:20:54 +0000243 float brightness, boolean userChangedBrightness, float adjustment,
244 boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
Filip Gruszczynskia15aa7d2014-10-28 14:12:40 -0700245 // While dozing, the application processor may be suspended which will prevent us from
246 // receiving new information from the light sensor. On some devices, we may be able to
247 // switch to a wake-up light sensor instead but for now we will simply disable the sensor
248 // and hold onto the last computed screen auto brightness. We save the dozing flag for
249 // debugging purposes.
Michael Wrightd8460232018-01-16 18:04:59 +0000250 boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
Michael Wrighteef0e132017-11-21 17:57:52 +0000251 boolean changed = setBrightnessConfiguration(configuration);
Michael Wrightd8460232018-01-16 18:04:59 +0000252 changed |= setDisplayPolicy(displayPolicy);
Dan Gittik57d6f112018-03-27 18:14:22 +0100253 if (userChangedAutoBrightnessAdjustment) {
254 changed |= setAutoBrightnessAdjustment(adjustment);
255 }
Michael Wright617564f2018-01-25 22:20:54 +0000256 if (userChangedBrightness && enable) {
257 // Update the brightness curve with the new user control point. It's critical this
258 // happens after we update the autobrightness adjustment since it may reset it.
Michael Wrightd8460232018-01-16 18:04:59 +0000259 changed |= setScreenBrightnessByUser(brightness);
Michael Wright617564f2018-01-25 22:20:54 +0000260 }
261 final boolean userInitiatedChange =
262 userChangedBrightness || userChangedAutoBrightnessAdjustment;
263 if (userInitiatedChange && enable && !dozing) {
Soroosh Mariooryada8edffd2017-03-22 17:08:26 -0700264 prepareBrightnessAdjustmentSample();
265 }
Michael Wrightd8460232018-01-16 18:04:59 +0000266 changed |= setLightSensorEnabled(enable && !dozing);
Jeff Brown970d4132014-07-19 11:33:47 -0700267 if (changed) {
Michael Wright639c8be2014-01-17 18:29:12 -0800268 updateAutoBrightness(false /*sendUpdate*/);
269 }
270 }
271
Kenny Guy53d06612018-01-30 14:19:13 +0000272 public boolean hasUserDataPoints() {
273 return mBrightnessMapper.hasUserDataPoints();
274 }
275
276 public boolean isDefaultConfig() {
277 return mBrightnessMapper.isDefaultConfig();
278 }
279
Kenny Guy6d1009f2018-03-14 14:28:23 +0000280 public BrightnessConfiguration getDefaultConfig() {
281 return mBrightnessMapper.getDefaultConfig();
282 }
283
Michael Wrightd8460232018-01-16 18:04:59 +0000284 private boolean setDisplayPolicy(int policy) {
285 if (mDisplayPolicy == policy) {
286 return false;
287 }
288 final int oldPolicy = mDisplayPolicy;
289 mDisplayPolicy = policy;
290 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000291 Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
Michael Wrightd8460232018-01-16 18:04:59 +0000292 }
293 if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000294 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
Michael Wrightd8460232018-01-16 18:04:59 +0000295 SHORT_TERM_MODEL_TIMEOUT_MILLIS);
296 } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000297 mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
Michael Wrightd8460232018-01-16 18:04:59 +0000298 }
299 return true;
300 }
301
302 private static boolean isInteractivePolicy(int policy) {
303 return policy == DisplayPowerRequest.POLICY_BRIGHT
304 || policy == DisplayPowerRequest.POLICY_DIM
305 || policy == DisplayPowerRequest.POLICY_VR;
306 }
307
308 private boolean setScreenBrightnessByUser(float brightness) {
309 if (!mAmbientLuxValid) {
310 // If we don't have a valid ambient lux then we don't have a valid brightness anyways,
311 // and we can't use this data to add a new control point to the short-term model.
312 return false;
313 }
314 mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
Dan Gittikc5d32912018-02-22 13:53:47 +0000315 mShortTermModelValid = true;
316 mShortTermModelAnchor = mAmbientLux;
317 if (DEBUG) {
318 Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
319 }
Michael Wrightd8460232018-01-16 18:04:59 +0000320 return true;
321 }
322
Dan Gittikc1352252018-04-27 17:48:31 +0100323 public void resetShortTermModel() {
Michael Wrightd8460232018-01-16 18:04:59 +0000324 mBrightnessMapper.clearUserDataPoints();
Dan Gittikc5d32912018-02-22 13:53:47 +0000325 mShortTermModelValid = true;
326 mShortTermModelAnchor = -1;
327 }
328
329 private void invalidateShortTermModel() {
330 if (DEBUG) {
331 Slog.d(TAG, "ShortTermModel: invalidate user data");
332 }
333 mShortTermModelValid = false;
Michael Wrightd8460232018-01-16 18:04:59 +0000334 }
335
Michael Wrighteef0e132017-11-21 17:57:52 +0000336 public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
Michael Wrightb8f4f772018-05-01 18:28:58 +0100337 if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
338 resetShortTermModel();
339 return true;
340 }
341 return false;
Michael Wrighteef0e132017-11-21 17:57:52 +0000342 }
343
Michael Wright639c8be2014-01-17 18:29:12 -0800344 public void dump(PrintWriter pw) {
345 pw.println();
346 pw.println("Automatic Brightness Controller Configuration:");
Michael Wright639c8be2014-01-17 18:29:12 -0800347 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
348 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
Michael Wright2155bc22018-05-01 00:38:32 +0100349 pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
350 pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
351 pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800352 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800353 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
354 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
355 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
Michael Wright2155bc22018-05-01 00:38:32 +0100356 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
357 pw.println(" mWeightingIntercept=" + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800358
359 pw.println();
360 pw.println("Automatic Brightness Controller State:");
361 pw.println(" mLightSensor=" + mLightSensor);
Michael Wright639c8be2014-01-17 18:29:12 -0800362 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
363 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
Michael Wright2155bc22018-05-01 00:38:32 +0100364 pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800365 pw.println(" mAmbientLux=" + mAmbientLux);
Dan Gittik58784422018-04-05 12:30:38 +0100366 pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
Michael Wright639c8be2014-01-17 18:29:12 -0800367 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
368 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
369 pw.println(" mLastObservedLux=" + mLastObservedLux);
370 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
371 pw.println(" mRecentLightSamples=" + mRecentLightSamples);
372 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
373 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
Michael Wright2155bc22018-05-01 00:38:32 +0100374 pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
Dan Gittikc5d32912018-02-22 13:53:47 +0000375 pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
Michael Wright2155bc22018-05-01 00:38:32 +0100376 pw.println(" mShortTermModelValid=" + mShortTermModelValid);
377 pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
378 pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
379 pw.println(" mBrightnessAdjustmentSampleOldBrightness="
380 + mBrightnessAdjustmentSampleOldBrightness);
381 pw.println(" mShortTermModelValid=" + mShortTermModelValid);
Michael Wrighteef0e132017-11-21 17:57:52 +0000382
383 pw.println();
384 mBrightnessMapper.dump(pw);
Michael Wright2155bc22018-05-01 00:38:32 +0100385
386 pw.println();
387 mHysteresisLevels.dump(pw);
Michael Wright639c8be2014-01-17 18:29:12 -0800388 }
389
390 private boolean setLightSensorEnabled(boolean enable) {
391 if (enable) {
392 if (!mLightSensorEnabled) {
393 mLightSensorEnabled = true;
394 mLightSensorEnableTime = SystemClock.uptimeMillis();
Julius D'souza5d717092016-10-24 19:26:45 -0700395 mCurrentLightSensorRate = mInitialLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800396 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
Julius D'souza5d717092016-10-24 19:26:45 -0700397 mCurrentLightSensorRate * 1000, mHandler);
Michael Wright639c8be2014-01-17 18:29:12 -0800398 return true;
399 }
Dan Gittikc5d32912018-02-22 13:53:47 +0000400 } else if (mLightSensorEnabled) {
401 mLightSensorEnabled = false;
402 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
403 mRecentLightSamples = 0;
404 mAmbientLightRingBuffer.clear();
405 mCurrentLightSensorRate = -1;
406 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
407 mSensorManager.unregisterListener(mLightSensorListener);
Michael Wright639c8be2014-01-17 18:29:12 -0800408 }
409 return false;
410 }
411
412 private void handleLightSensorEvent(long time, float lux) {
Michael Wrightb0a1d3d2017-09-22 15:05:02 +0100413 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
Michael Wright639c8be2014-01-17 18:29:12 -0800414 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
415
Julius D'souza5d717092016-10-24 19:26:45 -0700416 if (mAmbientLightRingBuffer.size() == 0) {
417 // switch to using the steady-state sample rate after grabbing the initial light sample
418 adjustLightSensorRate(mNormalLightSensorRate);
419 }
Michael Wright639c8be2014-01-17 18:29:12 -0800420 applyLightSensorMeasurement(time, lux);
421 updateAmbientLux(time);
422 }
423
424 private void applyLightSensorMeasurement(long time, float lux) {
425 mRecentLightSamples++;
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100426 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800427 mAmbientLightRingBuffer.push(time, lux);
428
429 // Remember this sample value.
430 mLastObservedLux = lux;
431 mLastObservedLuxTime = time;
432 }
433
Julius D'souza5d717092016-10-24 19:26:45 -0700434 private void adjustLightSensorRate(int lightSensorRate) {
435 // if the light sensor rate changed, update the sensor listener
436 if (lightSensorRate != mCurrentLightSensorRate) {
437 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000438 Slog.d(TAG, "adjustLightSensorRate: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100439 "previousRate=" + mCurrentLightSensorRate + ", " +
440 "currentRate=" + lightSensorRate);
Julius D'souza5d717092016-10-24 19:26:45 -0700441 }
442 mCurrentLightSensorRate = lightSensorRate;
443 mSensorManager.unregisterListener(mLightSensorListener);
444 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
445 lightSensorRate * 1000, mHandler);
446 }
447 }
448
Dan Gittik57d6f112018-03-27 18:14:22 +0100449 private boolean setAutoBrightnessAdjustment(float adjustment) {
450 return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
Michael Wright639c8be2014-01-17 18:29:12 -0800451 }
452
453 private void setAmbientLux(float lux) {
Michael Wright0a933142017-08-16 20:38:21 +0100454 if (DEBUG) {
455 Slog.d(TAG, "setAmbientLux(" + lux + ")");
456 }
Michael Wrightd8460232018-01-16 18:04:59 +0000457 if (lux < 0) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000458 Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
Michael Wrightd8460232018-01-16 18:04:59 +0000459 lux = 0;
460 }
Michael Wright639c8be2014-01-17 18:29:12 -0800461 mAmbientLux = lux;
Michael Wright2155bc22018-05-01 00:38:32 +0100462 mBrighteningLuxThreshold = mHysteresisLevels.getBrighteningThreshold(lux);
463 mDarkeningLuxThreshold = mHysteresisLevels.getDarkeningThreshold(lux);
Dan Gittikc5d32912018-02-22 13:53:47 +0000464
465 // If the short term model was invalidated and the change is drastic enough, reset it.
466 if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
467 final float minAmbientLux =
468 mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
469 final float maxAmbientLux =
470 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
471 if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
Dan Gittik57d6f112018-03-27 18:14:22 +0100472 if (DEBUG) {
473 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
474 minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
475 }
Dan Gittikc5d32912018-02-22 13:53:47 +0000476 mShortTermModelValid = true;
477 } else {
478 Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + mAmbientLux +
Dan Gittik57d6f112018-03-27 18:14:22 +0100479 "(" + minAmbientLux + ", " + maxAmbientLux + ")");
Dan Gittikc5d32912018-02-22 13:53:47 +0000480 resetShortTermModel();
481 }
482 }
Michael Wright639c8be2014-01-17 18:29:12 -0800483 }
484
Michael Wright0a933142017-08-16 20:38:21 +0100485 private float calculateAmbientLux(long now, long horizon) {
486 if (DEBUG) {
487 Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
488 }
Michael Wright639c8be2014-01-17 18:29:12 -0800489 final int N = mAmbientLightRingBuffer.size();
490 if (N == 0) {
491 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
492 return -1;
493 }
Michael Wright0a933142017-08-16 20:38:21 +0100494
495 // Find the first measurement that is just outside of the horizon.
496 int endIndex = 0;
497 final long horizonStartTime = now - horizon;
498 for (int i = 0; i < N-1; i++) {
499 if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
500 endIndex++;
501 } else {
502 break;
503 }
504 }
505 if (DEBUG) {
Dan Gittik57d6f112018-03-27 18:14:22 +0100506 Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
507 mAmbientLightRingBuffer.getTime(endIndex) + ", " +
508 mAmbientLightRingBuffer.getLux(endIndex) + ")");
Michael Wright0a933142017-08-16 20:38:21 +0100509 }
Michael Wright639c8be2014-01-17 18:29:12 -0800510 float sum = 0;
511 float totalWeight = 0;
512 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
Michael Wright0a933142017-08-16 20:38:21 +0100513 for (int i = N - 1; i >= endIndex; i--) {
514 long eventTime = mAmbientLightRingBuffer.getTime(i);
515 if (i == endIndex && eventTime < horizonStartTime) {
516 // If we're at the final value, make sure we only consider the part of the sample
517 // within our desired horizon.
518 eventTime = horizonStartTime;
519 }
520 final long startTime = eventTime - now;
Michael Wright639c8be2014-01-17 18:29:12 -0800521 float weight = calculateWeight(startTime, endTime);
522 float lux = mAmbientLightRingBuffer.getLux(i);
523 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000524 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100525 "lux=" + lux + ", " +
526 "weight=" + weight);
Michael Wright639c8be2014-01-17 18:29:12 -0800527 }
528 totalWeight += weight;
Dan Gittikc5d32912018-02-22 13:53:47 +0000529 sum += lux * weight;
Michael Wright639c8be2014-01-17 18:29:12 -0800530 endTime = startTime;
531 }
532 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000533 Slog.d(TAG, "calculateAmbientLux: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100534 "totalWeight=" + totalWeight + ", " +
535 "newAmbientLux=" + (sum / totalWeight));
Michael Wright639c8be2014-01-17 18:29:12 -0800536 }
537 return sum / totalWeight;
538 }
539
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100540 private float calculateWeight(long startDelta, long endDelta) {
Michael Wright639c8be2014-01-17 18:29:12 -0800541 return weightIntegral(endDelta) - weightIntegral(startDelta);
542 }
543
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100544 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
Michael Wright639c8be2014-01-17 18:29:12 -0800545 // horizon we're looking at and provides a non-linear weighting for light samples.
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100546 private float weightIntegral(long x) {
547 return x * (x * 0.5f + mWeightingIntercept);
Michael Wright639c8be2014-01-17 18:29:12 -0800548 }
549
550 private long nextAmbientLightBrighteningTransition(long time) {
551 final int N = mAmbientLightRingBuffer.size();
552 long earliestValidTime = time;
553 for (int i = N - 1; i >= 0; i--) {
554 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
555 break;
556 }
557 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
558 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800559 return earliestValidTime + mBrighteningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800560 }
561
562 private long nextAmbientLightDarkeningTransition(long time) {
563 final int N = mAmbientLightRingBuffer.size();
564 long earliestValidTime = time;
565 for (int i = N - 1; i >= 0; i--) {
566 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
567 break;
568 }
569 earliestValidTime = mAmbientLightRingBuffer.getTime(i);
570 }
Filip Gruszczynskid81ecd12015-02-06 12:38:47 -0800571 return earliestValidTime + mDarkeningLightDebounceConfig;
Michael Wright639c8be2014-01-17 18:29:12 -0800572 }
573
574 private void updateAmbientLux() {
575 long time = SystemClock.uptimeMillis();
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100576 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
Michael Wright639c8be2014-01-17 18:29:12 -0800577 updateAmbientLux(time);
578 }
579
580 private void updateAmbientLux(long time) {
581 // If the light sensor was just turned on then immediately update our initial
582 // estimate of the current ambient light level.
583 if (!mAmbientLuxValid) {
584 final long timeWhenSensorWarmedUp =
585 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
586 if (time < timeWhenSensorWarmedUp) {
587 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000588 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100589 "time=" + time + ", " +
590 "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
Michael Wright639c8be2014-01-17 18:29:12 -0800591 }
592 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
593 timeWhenSensorWarmedUp);
594 return;
595 }
Michael Wright0a933142017-08-16 20:38:21 +0100596 setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
Michael Wright639c8be2014-01-17 18:29:12 -0800597 mAmbientLuxValid = true;
598 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000599 Slog.d(TAG, "updateAmbientLux: Initializing: " +
600 "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
601 "mAmbientLux=" + mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800602 }
603 updateAutoBrightness(true);
604 }
605
606 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
607 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
Michael Wright0a933142017-08-16 20:38:21 +0100608 // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
609 // change in lighting conditions, and a fast ambient lux to determine what the new
610 // brightness situation is since the slow lux can be quite slow to converge.
611 //
612 // Note that both values need to be checked for sufficient change before updating the
613 // proposed ambient light value since the slow value might be sufficiently far enough away
614 // from the fast value to cause a recalculation while its actually just converging on
615 // the fast value still.
616 float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
617 float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
Michael Wright639c8be2014-01-17 18:29:12 -0800618
Dan Gittikc5d32912018-02-22 13:53:47 +0000619 if ((slowAmbientLux >= mBrighteningLuxThreshold &&
620 fastAmbientLux >= mBrighteningLuxThreshold &&
621 nextBrightenTransition <= time)
622 ||
623 (slowAmbientLux <= mDarkeningLuxThreshold &&
624 fastAmbientLux <= mDarkeningLuxThreshold &&
625 nextDarkenTransition <= time)) {
Michael Wright0a933142017-08-16 20:38:21 +0100626 setAmbientLux(fastAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800627 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000628 Slog.d(TAG, "updateAmbientLux: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100629 ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " +
630 "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold + ", " +
631 "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
632 "mAmbientLux=" + mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800633 }
634 updateAutoBrightness(true);
635 nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
636 nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
637 }
638 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
639 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
640 // exceed the necessary threshold, then it's possible we'll get a transition time prior to
641 // now. Rather than continually checking to see whether the weighted lux exceeds the
642 // threshold, schedule an update for when we'd normally expect another light sample, which
643 // should be enough time to decide whether we should actually transition to the new
644 // weighted ambient lux or not.
645 nextTransitionTime =
Julius D'souza5d717092016-10-24 19:26:45 -0700646 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
Michael Wright639c8be2014-01-17 18:29:12 -0800647 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000648 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100649 nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
Michael Wright639c8be2014-01-17 18:29:12 -0800650 }
651 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
652 }
653
654 private void updateAutoBrightness(boolean sendUpdate) {
655 if (!mAmbientLuxValid) {
656 return;
657 }
658
Michael Wrighteef0e132017-11-21 17:57:52 +0000659 float value = mBrightnessMapper.getBrightness(mAmbientLux);
Michael Wright639c8be2014-01-17 18:29:12 -0800660
661 int newScreenAutoBrightness =
Jeff Browna576b4d2015-04-23 19:58:06 -0700662 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
Michael Wright639c8be2014-01-17 18:29:12 -0800663 if (mScreenAutoBrightness != newScreenAutoBrightness) {
664 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000665 Slog.d(TAG, "updateAutoBrightness: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100666 "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
667 "newScreenAutoBrightness=" + newScreenAutoBrightness);
Michael Wright639c8be2014-01-17 18:29:12 -0800668 }
669
670 mScreenAutoBrightness = newScreenAutoBrightness;
Michael Wright639c8be2014-01-17 18:29:12 -0800671 if (sendUpdate) {
672 mCallbacks.updateBrightness();
673 }
674 }
675 }
676
677 private int clampScreenBrightness(int value) {
678 return MathUtils.constrain(value,
679 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
680 }
681
Jeff Browna576b4d2015-04-23 19:58:06 -0700682 private void prepareBrightnessAdjustmentSample() {
683 if (!mBrightnessAdjustmentSamplePending) {
684 mBrightnessAdjustmentSamplePending = true;
Jeff Browna576b4d2015-04-23 19:58:06 -0700685 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
686 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
Jeff Browna576b4d2015-04-23 19:58:06 -0700687 } else {
688 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
689 }
690
691 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
692 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
693 }
694
695 private void cancelBrightnessAdjustmentSample() {
696 if (mBrightnessAdjustmentSamplePending) {
697 mBrightnessAdjustmentSamplePending = false;
698 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
699 }
700 }
701
702 private void collectBrightnessAdjustmentSample() {
703 if (mBrightnessAdjustmentSamplePending) {
704 mBrightnessAdjustmentSamplePending = false;
705 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
706 if (DEBUG) {
Dan Gittikc5d32912018-02-22 13:53:47 +0000707 Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
Dan Gittik57d6f112018-03-27 18:14:22 +0100708 "lux=" + mAmbientLux + ", " +
709 "brightness=" + mScreenAutoBrightness + ", " +
710 "ring=" + mAmbientLightRingBuffer);
Jeff Browna576b4d2015-04-23 19:58:06 -0700711 }
712
713 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
Jeff Browna576b4d2015-04-23 19:58:06 -0700714 mBrightnessAdjustmentSampleOldLux,
715 mBrightnessAdjustmentSampleOldBrightness,
Jeff Browna576b4d2015-04-23 19:58:06 -0700716 mAmbientLux,
Dan Gittik57d6f112018-03-27 18:14:22 +0100717 mScreenAutoBrightness);
Jeff Browna576b4d2015-04-23 19:58:06 -0700718 }
719 }
720 }
721
Michael Wright639c8be2014-01-17 18:29:12 -0800722 private final class AutomaticBrightnessHandler extends Handler {
723 public AutomaticBrightnessHandler(Looper looper) {
724 super(looper, null, true /*async*/);
725 }
726
727 @Override
728 public void handleMessage(Message msg) {
729 switch (msg.what) {
730 case MSG_UPDATE_AMBIENT_LUX:
731 updateAmbientLux();
732 break;
Jeff Browna576b4d2015-04-23 19:58:06 -0700733
734 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
735 collectBrightnessAdjustmentSample();
736 break;
Michael Wrightd8460232018-01-16 18:04:59 +0000737
Dan Gittikc5d32912018-02-22 13:53:47 +0000738 case MSG_INVALIDATE_SHORT_TERM_MODEL:
739 invalidateShortTermModel();
Michael Wrightd8460232018-01-16 18:04:59 +0000740 break;
Michael Wright639c8be2014-01-17 18:29:12 -0800741 }
742 }
743 }
744
745 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
746 @Override
747 public void onSensorChanged(SensorEvent event) {
748 if (mLightSensorEnabled) {
749 final long time = SystemClock.uptimeMillis();
750 final float lux = event.values[0];
751 handleLightSensorEvent(time, lux);
752 }
753 }
754
755 @Override
756 public void onAccuracyChanged(Sensor sensor, int accuracy) {
757 // Not used.
758 }
759 };
760
Michael Wright639c8be2014-01-17 18:29:12 -0800761 /** Callbacks to request updates to the display's power state. */
762 interface Callbacks {
763 void updateBrightness();
764 }
765
Michael Wright0a933142017-08-16 20:38:21 +0100766 /**
767 * A ring buffer of ambient light measurements sorted by time.
768 *
769 * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
770 * from oldest to newest.
771 */
Michael Wright103fb782016-04-22 01:03:09 -0400772 private static final class AmbientLightRingBuffer {
Michael Wright639c8be2014-01-17 18:29:12 -0800773 // Proportional extra capacity of the buffer beyond the expected number of light samples
774 // in the horizon
775 private static final float BUFFER_SLACK = 1.5f;
Michael Wright639c8be2014-01-17 18:29:12 -0800776 private float[] mRingLux;
777 private long[] mRingTime;
778 private int mCapacity;
779
780 // The first valid element and the next open slot.
781 // Note that if mCount is zero then there are no valid elements.
782 private int mStart;
783 private int mEnd;
784 private int mCount;
785
Zoran Jovanovic6fc42f52015-12-10 17:01:16 +0100786 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
787 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
Michael Wright639c8be2014-01-17 18:29:12 -0800788 mRingLux = new float[mCapacity];
789 mRingTime = new long[mCapacity];
790 }
791
792 public float getLux(int index) {
793 return mRingLux[offsetOf(index)];
794 }
795
796 public long getTime(int index) {
797 return mRingTime[offsetOf(index)];
798 }
799
800 public void push(long time, float lux) {
801 int next = mEnd;
802 if (mCount == mCapacity) {
803 int newSize = mCapacity * 2;
804
805 float[] newRingLux = new float[newSize];
806 long[] newRingTime = new long[newSize];
807 int length = mCapacity - mStart;
808 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
809 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
810 if (mStart != 0) {
811 System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
812 System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
813 }
814 mRingLux = newRingLux;
815 mRingTime = newRingTime;
816
817 next = mCapacity;
818 mCapacity = newSize;
819 mStart = 0;
820 }
821 mRingTime[next] = time;
822 mRingLux[next] = lux;
823 mEnd = next + 1;
824 if (mEnd == mCapacity) {
825 mEnd = 0;
826 }
827 mCount++;
828 }
829
830 public void prune(long horizon) {
831 if (mCount == 0) {
832 return;
833 }
834
835 while (mCount > 1) {
836 int next = mStart + 1;
837 if (next >= mCapacity) {
838 next -= mCapacity;
839 }
840 if (mRingTime[next] > horizon) {
841 // Some light sensors only produce data upon a change in the ambient light
842 // levels, so we need to consider the previous measurement as the ambient light
843 // level for all points in time up until we receive a new measurement. Thus, we
844 // always want to keep the youngest element that would be removed from the
845 // buffer and just set its measurement time to the horizon time since at that
846 // point it is the ambient light level, and to remove it would be to drop a
847 // valid data point within our horizon.
848 break;
849 }
850 mStart = next;
851 mCount -= 1;
852 }
853
854 if (mRingTime[mStart] < horizon) {
855 mRingTime[mStart] = horizon;
856 }
857 }
858
859 public int size() {
860 return mCount;
861 }
862
Michael Wright639c8be2014-01-17 18:29:12 -0800863 public void clear() {
864 mStart = 0;
865 mEnd = 0;
866 mCount = 0;
867 }
868
869 @Override
870 public String toString() {
Jeff Browna576b4d2015-04-23 19:58:06 -0700871 StringBuffer buf = new StringBuffer();
872 buf.append('[');
873 for (int i = 0; i < mCount; i++) {
874 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
875 if (i != 0) {
876 buf.append(", ");
877 }
878 buf.append(getLux(i));
879 buf.append(" / ");
880 buf.append(next - getTime(i));
881 buf.append("ms");
Michael Wright639c8be2014-01-17 18:29:12 -0800882 }
Jeff Browna576b4d2015-04-23 19:58:06 -0700883 buf.append(']');
884 return buf.toString();
Michael Wright639c8be2014-01-17 18:29:12 -0800885 }
886
887 private int offsetOf(int index) {
888 if (index >= mCount || index < 0) {
889 throw new ArrayIndexOutOfBoundsException(index);
890 }
891 index += mStart;
892 if (index >= mCapacity) {
893 index -= mCapacity;
894 }
895 return index;
896 }
897 }
898}