1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX;
20 import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.ActivityTaskManager;
26 import android.app.ActivityTaskManager.RootTaskInfo;
27 import android.app.IActivityTaskManager;
28 import android.app.TaskStackListener;
29 import android.content.Context;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageManager;
32 import android.hardware.Sensor;
33 import android.hardware.SensorEvent;
34 import android.hardware.SensorEventListener;
35 import android.hardware.SensorManager;
36 import android.hardware.display.BrightnessConfiguration;
37 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.Message;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.os.SystemClock;
44 import android.os.Trace;
45 import android.util.EventLog;
46 import android.util.MathUtils;
47 import android.util.Slog;
48 import android.util.SparseArray;
49 import android.util.TimeUtils;
50 import android.view.Display;
51 
52 import com.android.internal.annotations.VisibleForTesting;
53 import com.android.internal.display.BrightnessSynchronizer;
54 import com.android.internal.os.BackgroundThread;
55 import com.android.server.EventLogTags;
56 import com.android.server.display.brightness.BrightnessEvent;
57 import com.android.server.display.config.HysteresisLevels;
58 import com.android.server.display.feature.DisplayManagerFlags;
59 
60 import java.io.PrintWriter;
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 import java.util.concurrent.TimeUnit;
64 
65 /**
66  * Manages the associated display brightness when in auto-brightness mode. This is also
67  * responsible for managing the brightness lux-nits mapping strategies. Internally also listens to
68  * the LightSensor and adjusts the system brightness in case of changes in the surrounding lux.
69  */
70 public class AutomaticBrightnessController {
71     private static final String TAG = "AutomaticBrightnessController";
72 
73     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
74 
75     public static final int AUTO_BRIGHTNESS_ENABLED = 1;
76     public static final int AUTO_BRIGHTNESS_DISABLED = 2;
77     public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3;
78 
79     @IntDef(prefix = { "AUTO_BRIGHTNESS_MODE_" }, value = {
80             AUTO_BRIGHTNESS_MODE_DEFAULT,
81             AUTO_BRIGHTNESS_MODE_IDLE,
82             AUTO_BRIGHTNESS_MODE_DOZE
83     })
84     @Retention(RetentionPolicy.SOURCE)
85     public @interface AutomaticBrightnessMode{}
86 
87     public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0;
88     public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1;
89     public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2;
90     public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE;
91 
92     // How long the current sensor reading is assumed to be valid beyond the current time.
93     // This provides a bit of prediction, as well as ensures that the weight for the last sample is
94     // non-zero, which in turn ensures that the total weight is non-zero.
95     private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
96 
97     // Debounce for sampling user-initiated changes in display brightness to ensure
98     // the user is satisfied with the result before storing the sample.
99     private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
100 
101     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
102     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
103     private static final int MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL = 3;
104     private static final int MSG_UPDATE_FOREGROUND_APP = 4;
105     private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
106     private static final int MSG_RUN_UPDATE = 6;
107     private static final int MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL = 7;
108 
109     // Callbacks for requesting updates to the display's power state
110     private final Callbacks mCallbacks;
111 
112     // The sensor manager.
113     private final SensorManager mSensorManager;
114 
115     // The light sensor, or null if not available or needed.
116     private final Sensor mLightSensor;
117 
118     // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
119     @NonNull
120     private BrightnessMappingStrategy mCurrentBrightnessMapper;
121 
122     // A map of Brightness Mapping Strategies indexed by AutomaticBrightnessMode
123     private final SparseArray<BrightnessMappingStrategy> mBrightnessMappingStrategyMap;
124 
125     // The minimum and maximum screen brightnesses.
126     private final float mScreenBrightnessRangeMinimum;
127     private final float mScreenBrightnessRangeMaximum;
128 
129     // How much to scale doze brightness by (should be (0, 1.0]).
130     private final float mDozeScaleFactor;
131 
132     // Initial light sensor event rate in milliseconds.
133     private final int mInitialLightSensorRate;
134 
135     // Steady-state light sensor event rate in milliseconds.
136     private final int mNormalLightSensorRate;
137 
138     // The current light sensor event rate in milliseconds.
139     private int mCurrentLightSensorRate;
140 
141     // Stability requirements in milliseconds for accepting a new brightness level.  This is used
142     // for debouncing the light sensor.  Different constants are used to debounce the light sensor
143     // when adapting to brighter or darker environments.  This parameter controls how quickly
144     // brightness changes occur in response to an observed change in light level that exceeds the
145     // hysteresis threshold.
146     private final long mBrighteningLightDebounceConfig;
147     private final long mDarkeningLightDebounceConfig;
148     private final long mBrighteningLightDebounceConfigIdle;
149     private final long mDarkeningLightDebounceConfigIdle;
150 
151     // If true immediately after the screen is turned on the controller will try to adjust the
152     // brightness based on the current sensor reads. If false, the controller will collect more data
153     // and only then decide whether to change brightness.
154     private final boolean mResetAmbientLuxAfterWarmUpConfig;
155 
156     // Period of time in which to consider light samples for a short/long-term estimate of ambient
157     // light in milliseconds.
158     private final int mAmbientLightHorizonLong;
159     private final int mAmbientLightHorizonShort;
160 
161     // The intercept used for the weighting calculation. This is used in order to keep all possible
162     // weighting values positive.
163     private final int mWeightingIntercept;
164 
165     // Configuration object for determining thresholds to change brightness dynamically
166     private final HysteresisLevels mAmbientBrightnessThresholds;
167     private final HysteresisLevels mScreenBrightnessThresholds;
168     private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
169     private final HysteresisLevels mScreenBrightnessThresholdsIdle;
170 
171     private boolean mLoggingEnabled;
172 
173     // Amount of time to delay auto-brightness after screen on while waiting for
174     // the light sensor to warm-up in milliseconds.
175     // May be 0 if no warm-up is required.
176     private int mLightSensorWarmUpTimeConfig;
177 
178     // Set to true if the light sensor is enabled.
179     private boolean mLightSensorEnabled;
180 
181     // The time when the light sensor was enabled.
182     private long mLightSensorEnableTime;
183 
184     // The currently accepted nominal ambient light level.
185     private float mAmbientLux = INVALID_LUX;
186 
187     // The last calculated ambient light level (long time window).
188     private float mSlowAmbientLux;
189 
190     // The last calculated ambient light level (short time window).
191     private float mFastAmbientLux;
192 
193     // The last ambient lux value prior to passing the darkening or brightening threshold.
194     private float mPreThresholdLux;
195 
196     // True if mAmbientLux holds a valid value.
197     private boolean mAmbientLuxValid;
198 
199     // The ambient light level threshold at which to brighten or darken the screen.
200     private float mAmbientBrighteningThreshold;
201     private float mAmbientDarkeningThreshold;
202 
203     // The last brightness value prior to passing the darkening or brightening threshold.
204     private float mPreThresholdBrightness;
205 
206     // The screen brightness threshold at which to brighten or darken the screen.
207     private float mScreenBrighteningThreshold;
208     private float mScreenDarkeningThreshold;
209     // The most recent light sample.
210     private float mLastObservedLux;
211 
212     // The time of the most light recent sample.
213     private long mLastObservedLuxTime;
214 
215     // The number of light samples collected since the light sensor was enabled.
216     private int mRecentLightSamples;
217 
218     // A ring buffer containing all of the recent ambient light sensor readings.
219     private AmbientLightRingBuffer mAmbientLightRingBuffer;
220 
221     // The handler
222     private AutomaticBrightnessHandler mHandler;
223 
224     // The screen brightness level that has been chosen by the auto-brightness
225     // algorithm.  The actual brightness should ramp towards this value.
226     // We preserve this value even when we stop using the light sensor so
227     // that we can quickly revert to the previous auto-brightness level
228     // while the light sensor warms up.
229     // Use PowerManager.BRIGHTNESS_INVALID_FLOAT if there is no current auto-brightness value
230     // available.
231     private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
232 
233     // The screen brightness level before clamping and throttling. This value needs to be stored
234     // for concurrent displays mode and passed to the additional displays which will do their own
235     // clamping and throttling.
236     private float mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
237 
238     // The current display policy. This is useful, for example,  for knowing when we're dozing,
239     // where the light sensor may not be available.
240     private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
241 
242     private int mDisplayState = Display.STATE_UNKNOWN;
243 
244     // True if we are collecting a brightness adjustment sample, along with some data
245     // for the initial state of the sample.
246     private boolean mBrightnessAdjustmentSamplePending;
247     private float mBrightnessAdjustmentSampleOldLux;
248     private float mBrightnessAdjustmentSampleOldBrightness;
249 
250     // The short term models, current and previous. Eg, we might use the "paused" one to save out
251     // the interactive short term model when switching to idle screen brightness mode, and
252     // vice-versa.
253     private final ShortTermModel mShortTermModel;
254     private final ShortTermModel mPausedShortTermModel;
255 
256     // Controls Brightness range (including High Brightness Mode).
257     private final BrightnessRangeController mBrightnessRangeController;
258 
259     // Throttles (caps) maximum allowed brightness
260     private final BrightnessThrottler mBrightnessThrottler;
261     private boolean mIsBrightnessThrottled;
262 
263     // Context-sensitive brightness configurations require keeping track of the foreground app's
264     // package name and category, which is done by registering a TaskStackListener to call back to
265     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
266     // package name and PackageManager to get its category (so might as well cache them).
267     private String mForegroundAppPackageName;
268     private String mPendingForegroundAppPackageName;
269     private @ApplicationInfo.Category int mForegroundAppCategory;
270     private @ApplicationInfo.Category int mPendingForegroundAppCategory;
271     private TaskStackListenerImpl mTaskStackListener;
272     private IActivityTaskManager mActivityTaskManager;
273     private PackageManager mPackageManager;
274     private Context mContext;
275     private int mState = AUTO_BRIGHTNESS_DISABLED;
276 
277     private Clock mClock;
278     private final Injector mInjector;
279 
280     private final DisplayManagerFlags mDisplayManagerFlags;
281 
AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessModeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, DisplayManagerFlags displayManagerFlags)282     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
283             SensorManager sensorManager, Sensor lightSensor,
284             SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
285             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
286             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
287             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
288             long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
289             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
290             HysteresisLevels screenBrightnessThresholds,
291             HysteresisLevels ambientBrightnessThresholdsIdle,
292             HysteresisLevels screenBrightnessThresholdsIdle, Context context,
293             BrightnessRangeController brightnessModeController,
294             BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
295             int ambientLightHorizonLong, float userLux, float userNits,
296             DisplayManagerFlags displayManagerFlags) {
297         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
298                 brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
299                 dozeScaleFactor, lightSensorRate, initialLightSensorRate,
300                 brighteningLightDebounceConfig, darkeningLightDebounceConfig,
301                 brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
302                 resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
303                 screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
304                 screenBrightnessThresholdsIdle, context, brightnessModeController,
305                 brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
306                 userNits, displayManagerFlags
307         );
308     }
309 
310     @VisibleForTesting
AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, DisplayManagerFlags displayManagerFlags)311     AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
312             SensorManager sensorManager, Sensor lightSensor,
313             SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
314             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
315             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
316             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
317             long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
318             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
319             HysteresisLevels screenBrightnessThresholds,
320             HysteresisLevels ambientBrightnessThresholdsIdle,
321             HysteresisLevels screenBrightnessThresholdsIdle, Context context,
322             BrightnessRangeController brightnessRangeController,
323             BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
324             int ambientLightHorizonLong, float userLux, float userNits,
325             DisplayManagerFlags displayManagerFlags) {
326         mInjector = injector;
327         mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
328         mContext = context;
329         mCallbacks = callbacks;
330         mSensorManager = sensorManager;
331         mCurrentBrightnessMapper = brightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT);
332         mScreenBrightnessRangeMinimum = brightnessMin;
333         mScreenBrightnessRangeMaximum = brightnessMax;
334         mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
335         mDozeScaleFactor = dozeScaleFactor;
336         mNormalLightSensorRate = lightSensorRate;
337         mInitialLightSensorRate = initialLightSensorRate;
338         mCurrentLightSensorRate = -1;
339         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
340         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
341         mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
342         mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
343         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
344         mAmbientLightHorizonLong = ambientLightHorizonLong;
345         mAmbientLightHorizonShort = ambientLightHorizonShort;
346         mWeightingIntercept = ambientLightHorizonLong;
347         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
348         mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
349         mScreenBrightnessThresholds = screenBrightnessThresholds;
350         mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
351         mShortTermModel = new ShortTermModel();
352         mPausedShortTermModel = new ShortTermModel();
353         mHandler = new AutomaticBrightnessHandler(looper);
354         mAmbientLightRingBuffer =
355             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
356 
357         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
358             mLightSensor = lightSensor;
359         }
360 
361         mActivityTaskManager = ActivityTaskManager.getService();
362         mPackageManager = mContext.getPackageManager();
363         mTaskStackListener = new TaskStackListenerImpl();
364         mForegroundAppPackageName = null;
365         mPendingForegroundAppPackageName = null;
366         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
367         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
368         mBrightnessRangeController = brightnessRangeController;
369         mBrightnessThrottler = brightnessThrottler;
370         mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
371         mDisplayManagerFlags = displayManagerFlags;
372 
373         // Use the given short-term model
374         if (userNits != BrightnessMappingStrategy.INVALID_NITS) {
375             setScreenBrightnessByUser(userLux, getBrightnessFromNits(userNits));
376         }
377     }
378 
379     /**
380      * Enable/disable logging.
381      *
382      * @param loggingEnabled
383      *      Whether logging should be on/off.
384      *
385      * @return Whether the method succeeded or not.
386      */
setLoggingEnabled(boolean loggingEnabled)387     public boolean setLoggingEnabled(boolean loggingEnabled) {
388         if (mLoggingEnabled == loggingEnabled) {
389             return false;
390         }
391         for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
392             mBrightnessMappingStrategyMap.valueAt(i).setLoggingEnabled(loggingEnabled);
393         }
394         mLoggingEnabled = loggingEnabled;
395         return true;
396     }
397 
getAutomaticScreenBrightness()398     public float getAutomaticScreenBrightness() {
399         return getAutomaticScreenBrightness(null);
400     }
401 
402     /**
403      * @param brightnessEvent Holds details about how the brightness is calculated.
404      *
405      * @return The current automatic brightness recommended value. Populates brightnessEvent
406      *         parameters with details about how the brightness was calculated.
407      */
getAutomaticScreenBrightness(BrightnessEvent brightnessEvent)408     public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
409         if (brightnessEvent != null) {
410             brightnessEvent.setLux(
411                     mAmbientLuxValid ? mAmbientLux : PowerManager.BRIGHTNESS_INVALID_FLOAT);
412             brightnessEvent.setPreThresholdLux(mPreThresholdLux);
413             brightnessEvent.setPreThresholdBrightness(mPreThresholdBrightness);
414             brightnessEvent.setRecommendedBrightness(mScreenAutoBrightness);
415             brightnessEvent.setFlags(brightnessEvent.getFlags()
416                     | (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
417                     | (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
418             brightnessEvent.setAutoBrightnessMode(getMode());
419         }
420 
421         if (!mAmbientLuxValid) {
422             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
423         }
424         if (shouldApplyDozeScaleFactor()) {
425             return mScreenAutoBrightness * mDozeScaleFactor;
426         }
427         return mScreenAutoBrightness;
428     }
429 
getRawAutomaticScreenBrightness()430     public float getRawAutomaticScreenBrightness() {
431         return mRawScreenAutoBrightness;
432     }
433 
hasValidAmbientLux()434     public boolean hasValidAmbientLux() {
435         return mAmbientLuxValid;
436     }
437 
getAutomaticScreenBrightnessAdjustment()438     public float getAutomaticScreenBrightnessAdjustment() {
439         return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
440     }
441 
configure(int state, @Nullable BrightnessConfiguration configuration, float brightness, boolean userChangedBrightness, float adjustment, boolean userChangedAutoBrightnessAdjustment, int displayPolicy, int displayState, boolean shouldResetShortTermModel)442     public void configure(int state, @Nullable BrightnessConfiguration configuration,
443             float brightness, boolean userChangedBrightness, float adjustment,
444             boolean userChangedAutoBrightnessAdjustment, int displayPolicy, int displayState,
445             boolean shouldResetShortTermModel) {
446         mState = state;
447         boolean changed = setBrightnessConfiguration(configuration, shouldResetShortTermModel);
448         changed |= setDisplayPolicy(displayPolicy);
449         mDisplayState = displayState;
450         if (userChangedAutoBrightnessAdjustment) {
451             changed |= setAutoBrightnessAdjustment(adjustment);
452         }
453         final boolean enable = mState == AUTO_BRIGHTNESS_ENABLED;
454         if (userChangedBrightness && enable) {
455             // Update the brightness curve with the new user control point. It's critical this
456             // happens after we update the autobrightness adjustment since it may reset it.
457             changed |= setScreenBrightnessByUser(brightness);
458         }
459         final boolean userInitiatedChange =
460                 userChangedBrightness || userChangedAutoBrightnessAdjustment;
461         if (userInitiatedChange && enable) {
462             prepareBrightnessAdjustmentSample();
463         }
464         changed |= setLightSensorEnabled(enable);
465 
466         if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
467             // Maximum brightness has changed, so recalculate display brightness.
468             mIsBrightnessThrottled = mBrightnessThrottler.isThrottled();
469             changed = true;
470         }
471 
472         if (changed) {
473             updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange);
474         }
475     }
476 
stop()477     public void stop() {
478         setLightSensorEnabled(false);
479     }
480 
hasUserDataPoints()481     public boolean hasUserDataPoints() {
482         return mCurrentBrightnessMapper.hasUserDataPoints();
483     }
484 
485     // Used internally to establish whether we have deviated from the default config.
isDefaultConfig()486     public boolean isDefaultConfig() {
487         return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_DEFAULT
488                 && mCurrentBrightnessMapper.isDefaultConfig();
489     }
490 
491     // Called from APIs to get the configuration.
getDefaultConfig()492     public BrightnessConfiguration getDefaultConfig() {
493         return mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT).getDefaultConfig();
494     }
495 
496     /**
497      * Force recalculate of the state of automatic brightness.
498      */
update()499     public void update() {
500         mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
501     }
502 
getAmbientLux()503     float getAmbientLux() {
504         return mAmbientLux;
505     }
506 
getSlowAmbientLux()507     float getSlowAmbientLux() {
508         return mSlowAmbientLux;
509     }
510 
getFastAmbientLux()511     float getFastAmbientLux() {
512         return mFastAmbientLux;
513     }
514 
setDisplayPolicy(int policy)515     private boolean setDisplayPolicy(int policy) {
516         if (mDisplayPolicy == policy) {
517             return false;
518         }
519         final int oldPolicy = mDisplayPolicy;
520         mDisplayPolicy = policy;
521         if (mLoggingEnabled) {
522             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
523         }
524         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) && !isInIdleMode()) {
525             mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL,
526                     mCurrentBrightnessMapper.getShortTermModelTimeout());
527         } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
528             mHandler.removeMessages(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL);
529         }
530         return true;
531     }
532 
isInteractivePolicy(int policy)533     private static boolean isInteractivePolicy(int policy) {
534         return policy == DisplayPowerRequest.POLICY_BRIGHT
535                 || policy == DisplayPowerRequest.POLICY_DIM;
536     }
537 
setScreenBrightnessByUser(float brightness)538     private boolean setScreenBrightnessByUser(float brightness) {
539         if (!mAmbientLuxValid) {
540             // If we don't have a valid ambient lux then we don't have a valid brightness anyway,
541             // and we can't use this data to add a new control point to the short-term model.
542             return false;
543         }
544         return setScreenBrightnessByUser(mAmbientLux, brightness);
545     }
546 
setScreenBrightnessByUser(float lux, float brightness)547     private boolean setScreenBrightnessByUser(float lux, float brightness) {
548         if (lux == INVALID_LUX || Float.isNaN(brightness)) {
549             return false;
550         }
551         mCurrentBrightnessMapper.addUserDataPoint(lux, brightness);
552         mShortTermModel.setUserBrightness(lux, brightness);
553         return true;
554     }
555 
resetShortTermModel()556     public void resetShortTermModel() {
557         mCurrentBrightnessMapper.clearUserDataPoints();
558         mShortTermModel.reset();
559     }
560 
setBrightnessConfiguration(BrightnessConfiguration configuration, boolean shouldResetShortTermModel)561     public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
562             boolean shouldResetShortTermModel) {
563         if (mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT)
564                 .setBrightnessConfiguration(configuration)) {
565             if (!isInIdleMode() && shouldResetShortTermModel) {
566                 resetShortTermModel();
567             }
568             return true;
569         }
570         return false;
571     }
572 
573     /**
574      * @return The auto-brightness mode of the current mapping strategy. Different modes use
575      * different brightness curves.
576      */
577     @AutomaticBrightnessController.AutomaticBrightnessMode
getMode()578     public int getMode() {
579         return mCurrentBrightnessMapper.getMode();
580     }
581 
isInIdleMode()582     public boolean isInIdleMode() {
583         return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE;
584     }
585 
dump(PrintWriter pw)586     public void dump(PrintWriter pw) {
587         pw.println();
588         pw.println("Automatic Brightness Controller Configuration:");
589         pw.println("  mState=" + configStateToString(mState));
590         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
591         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
592         pw.println("  mDozeScaleFactor=" + mDozeScaleFactor);
593         pw.println("  mInitialLightSensorRate=" + mInitialLightSensorRate);
594         pw.println("  mNormalLightSensorRate=" + mNormalLightSensorRate);
595         pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
596         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
597         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
598         pw.println("  mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
599         pw.println("  mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
600         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
601         pw.println("  mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
602         pw.println("  mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
603         pw.println("  mWeightingIntercept=" + mWeightingIntercept);
604 
605         pw.println();
606         pw.println("Automatic Brightness Controller State:");
607         pw.println("  mLightSensor=" + mLightSensor);
608         pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
609         pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
610         pw.println("  mCurrentLightSensorRate=" + mCurrentLightSensorRate);
611         pw.println("  mAmbientLux=" + mAmbientLux);
612         pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
613         pw.println("  mPreThresholdLux=" + mPreThresholdLux);
614         pw.println("  mPreThresholdBrightness=" + mPreThresholdBrightness);
615         pw.println("  mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
616         pw.println("  mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
617         pw.println("  mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
618         pw.println("  mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
619         pw.println("  mLastObservedLux=" + mLastObservedLux);
620         pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
621         pw.println("  mRecentLightSamples=" + mRecentLightSamples);
622         pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
623         pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
624         pw.println("  mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
625         pw.println("  mShortTermModel=");
626         mShortTermModel.dump(pw);
627         pw.println("  mPausedShortTermModel=");
628         mPausedShortTermModel.dump(pw);
629 
630         pw.println();
631         pw.println("  mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
632         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
633         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
634                 + mBrightnessAdjustmentSampleOldBrightness);
635         pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
636         pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
637         pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
638         pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
639         pw.println("  Current mode="
640                 + autoBrightnessModeToString(mCurrentBrightnessMapper.getMode()));
641 
642         for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
643             pw.println();
644             pw.println("  Mapper for mode "
645                     + autoBrightnessModeToString(mBrightnessMappingStrategyMap.keyAt(i)) + ":");
646             mBrightnessMappingStrategyMap.valueAt(i).dump(pw,
647                     mBrightnessRangeController.getNormalBrightnessMax());
648         }
649 
650         pw.println();
651         pw.println("  mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
652         pw.println("  mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
653         pw.println("  mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
654         pw.println("  mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
655     }
656 
getLastSensorValues()657     public float[] getLastSensorValues() {
658         return mAmbientLightRingBuffer.getAllLuxValues();
659     }
660 
getLastSensorTimestamps()661     public long[] getLastSensorTimestamps() {
662         return mAmbientLightRingBuffer.getAllTimestamps();
663     }
664 
configStateToString(int state)665     private String configStateToString(int state) {
666         switch (state) {
667         case AUTO_BRIGHTNESS_ENABLED:
668             return "AUTO_BRIGHTNESS_ENABLED";
669         case AUTO_BRIGHTNESS_DISABLED:
670             return "AUTO_BRIGHTNESS_DISABLED";
671         case AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE:
672             return "AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE";
673         default:
674             return String.valueOf(state);
675         }
676     }
677 
setLightSensorEnabled(boolean enable)678     private boolean setLightSensorEnabled(boolean enable) {
679         if (enable) {
680             if (!mLightSensorEnabled) {
681                 mLightSensorEnabled = true;
682                 mLightSensorEnableTime = mClock.uptimeMillis();
683                 mCurrentLightSensorRate = mInitialLightSensorRate;
684                 registerForegroundAppUpdater();
685                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
686                         mCurrentLightSensorRate * 1000, mHandler);
687                 return true;
688             }
689         } else if (mLightSensorEnabled) {
690             mLightSensorEnabled = false;
691             mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
692             if (!mAmbientLuxValid) {
693                 mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
694             }
695             mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
696             mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
697             mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
698             mRecentLightSamples = 0;
699             mAmbientLightRingBuffer.clear();
700             mCurrentLightSensorRate = -1;
701             mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
702             unregisterForegroundAppUpdater();
703             mSensorManager.unregisterListener(mLightSensorListener);
704         }
705         return false;
706     }
707 
handleLightSensorEvent(long time, float lux)708     private void handleLightSensorEvent(long time, float lux) {
709         Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
710         mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
711 
712         if (mAmbientLightRingBuffer.size() == 0) {
713             // switch to using the steady-state sample rate after grabbing the initial light sample
714             adjustLightSensorRate(mNormalLightSensorRate);
715         }
716         applyLightSensorMeasurement(time, lux);
717         updateAmbientLux(time);
718     }
719 
applyLightSensorMeasurement(long time, float lux)720     private void applyLightSensorMeasurement(long time, float lux) {
721         mRecentLightSamples++;
722         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
723         mAmbientLightRingBuffer.push(time, lux);
724         // Remember this sample value.
725         mLastObservedLux = lux;
726         mLastObservedLuxTime = time;
727     }
728 
adjustLightSensorRate(int lightSensorRate)729     private void adjustLightSensorRate(int lightSensorRate) {
730         // if the light sensor rate changed, update the sensor listener
731         if (lightSensorRate != mCurrentLightSensorRate) {
732             if (mLoggingEnabled) {
733                 Slog.d(TAG, "adjustLightSensorRate: " +
734                         "previousRate=" + mCurrentLightSensorRate + ", " +
735                         "currentRate=" + lightSensorRate);
736             }
737             mCurrentLightSensorRate = lightSensorRate;
738             mSensorManager.unregisterListener(mLightSensorListener);
739             mSensorManager.registerListener(mLightSensorListener, mLightSensor,
740                     lightSensorRate * 1000, mHandler);
741         }
742     }
743 
setAutoBrightnessAdjustment(float adjustment)744     private boolean setAutoBrightnessAdjustment(float adjustment) {
745         return mCurrentBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
746     }
747 
setAmbientLux(float lux)748     private void setAmbientLux(float lux) {
749         if (mLoggingEnabled) {
750             Slog.d(TAG, "setAmbientLux(" + lux + ")");
751         }
752         if (lux < 0) {
753             Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
754             lux = 0;
755         }
756         mAmbientLux = lux;
757         if (isInIdleMode()) {
758             mAmbientBrighteningThreshold =
759                     mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
760             mAmbientDarkeningThreshold =
761                     mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
762         } else {
763             mAmbientBrighteningThreshold =
764                     mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
765             mAmbientDarkeningThreshold =
766                     mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
767         }
768         mBrightnessRangeController.onAmbientLuxChange(mAmbientLux);
769 
770         // If the short term model was invalidated and the change is drastic enough, reset it.
771         mShortTermModel.maybeReset(mAmbientLux);
772     }
773 
calculateAmbientLux(long now, long horizon)774     private float calculateAmbientLux(long now, long horizon) {
775         if (mLoggingEnabled) {
776             Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
777         }
778         final int N = mAmbientLightRingBuffer.size();
779         if (N == 0) {
780             Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
781             return -1;
782         }
783 
784         // Find the first measurement that is just outside of the horizon.
785         int endIndex = 0;
786         final long horizonStartTime = now - horizon;
787         for (int i = 0; i < N-1; i++) {
788             if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
789                 endIndex++;
790             } else {
791                 break;
792             }
793         }
794         if (mLoggingEnabled) {
795             Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
796                     mAmbientLightRingBuffer.getTime(endIndex) + ", " +
797                     mAmbientLightRingBuffer.getLux(endIndex) + ")");
798         }
799         float sum = 0;
800         float totalWeight = 0;
801         long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
802         for (int i = N - 1; i >= endIndex; i--) {
803             long eventTime = mAmbientLightRingBuffer.getTime(i);
804             if (i == endIndex && eventTime < horizonStartTime) {
805                 // If we're at the final value, make sure we only consider the part of the sample
806                 // within our desired horizon.
807                 eventTime = horizonStartTime;
808             }
809             final long startTime = eventTime - now;
810             float weight = calculateWeight(startTime, endTime);
811             float lux = mAmbientLightRingBuffer.getLux(i);
812             if (mLoggingEnabled) {
813                 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
814                         "lux=" + lux + ", " +
815                         "weight=" + weight);
816             }
817             totalWeight += weight;
818             sum += lux * weight;
819             endTime = startTime;
820         }
821         if (mLoggingEnabled) {
822             Slog.d(TAG, "calculateAmbientLux: " +
823                     "totalWeight=" + totalWeight + ", " +
824                     "newAmbientLux=" + (sum / totalWeight));
825         }
826         return sum / totalWeight;
827     }
828 
calculateWeight(long startDelta, long endDelta)829     private float calculateWeight(long startDelta, long endDelta) {
830         return weightIntegral(endDelta) - weightIntegral(startDelta);
831     }
832 
833     // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
834     // horizon we're looking at and provides a non-linear weighting for light samples.
weightIntegral(long x)835     private float weightIntegral(long x) {
836         return x * (x * 0.5f + mWeightingIntercept);
837     }
838 
nextAmbientLightBrighteningTransition(long time)839     private long nextAmbientLightBrighteningTransition(long time) {
840         final int N = mAmbientLightRingBuffer.size();
841         long earliestValidTime = time;
842         for (int i = N - 1; i >= 0; i--) {
843             if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
844                 break;
845             }
846             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
847         }
848         return earliestValidTime + (isInIdleMode()
849                 ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig);
850     }
851 
nextAmbientLightDarkeningTransition(long time)852     private long nextAmbientLightDarkeningTransition(long time) {
853         final int N = mAmbientLightRingBuffer.size();
854         long earliestValidTime = time;
855         for (int i = N - 1; i >= 0; i--) {
856             if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
857                 break;
858             }
859             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
860         }
861         return earliestValidTime + (isInIdleMode()
862                 ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig);
863     }
864 
updateAmbientLux()865     private void updateAmbientLux() {
866         long time = mClock.getSensorEventScaleTime();
867         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
868         updateAmbientLux(time);
869     }
870 
updateAmbientLux(long time)871     private void updateAmbientLux(long time) {
872         // If the light sensor was just turned on then immediately update our initial
873         // estimate of the current ambient light level.
874         if (!mAmbientLuxValid) {
875             final long timeWhenSensorWarmedUp =
876                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
877             if (time < timeWhenSensorWarmedUp) {
878                 if (mLoggingEnabled) {
879                     Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
880                             + "time=" + time + ", "
881                             + "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
882                 }
883                 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
884                         timeWhenSensorWarmedUp);
885                 return;
886             }
887             setAmbientLux(calculateAmbientLux(time, mAmbientLightHorizonShort));
888             mAmbientLuxValid = true;
889             if (mLoggingEnabled) {
890                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
891                         "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
892                         "mAmbientLux=" + mAmbientLux);
893             }
894             updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
895         }
896 
897         long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
898         long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
899         // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
900         // change in lighting conditions, and a fast ambient lux to determine what the new
901         // brightness situation is since the slow lux can be quite slow to converge.
902         //
903         // Note that both values need to be checked for sufficient change before updating the
904         // proposed ambient light value since the slow value might be sufficiently far enough away
905         // from the fast value to cause a recalculation while its actually just converging on
906         // the fast value still.
907         mSlowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
908         mFastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
909 
910         if ((mSlowAmbientLux >= mAmbientBrighteningThreshold
911                 && mFastAmbientLux >= mAmbientBrighteningThreshold
912                 && nextBrightenTransition <= time)
913                 || (mSlowAmbientLux <= mAmbientDarkeningThreshold
914                         && mFastAmbientLux <= mAmbientDarkeningThreshold
915                         && nextDarkenTransition <= time)) {
916             mPreThresholdLux = mAmbientLux;
917             setAmbientLux(mFastAmbientLux);
918             if (mLoggingEnabled) {
919                 Slog.d(TAG, "updateAmbientLux: "
920                         + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
921                         + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
922                         + "mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold + ", "
923                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
924                         + "mAmbientLux=" + mAmbientLux);
925             }
926             updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
927             nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
928             nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
929         }
930         long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
931         // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
932         // exceed the necessary threshold, then it's possible we'll get a transition time prior to
933         // now. Rather than continually checking to see whether the weighted lux exceeds the
934         // threshold, schedule an update for when we'd normally expect another light sample, which
935         // should be enough time to decide whether we should actually transition to the new
936         // weighted ambient lux or not.
937         nextTransitionTime =
938                 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
939         if (mLoggingEnabled) {
940             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
941                     nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
942         }
943 
944         // The nextTransitionTime is computed as elapsedTime(Which also accounts for the time when
945         // android was sleeping) as the main reference. However, handlers work on the uptime(Not
946         // accounting for the time when android was sleeping)
947         mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
948                 convertToUptime(nextTransitionTime));
949     }
950 
convertToUptime(long time)951     private long convertToUptime(long time) {
952         return time - mClock.getSensorEventScaleTime() + mClock.uptimeMillis();
953     }
954 
updateAutoBrightness(boolean sendUpdate, boolean isManuallySet)955     private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
956         if (!mAmbientLuxValid) {
957             return;
958         }
959 
960         float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
961                 mForegroundAppCategory);
962         mRawScreenAutoBrightness = value;
963         float newScreenAutoBrightness = clampScreenBrightness(value);
964 
965         // The min/max range can change for brightness due to HBM. See if the current brightness
966         // value still falls within the current range (which could have changed).
967         final boolean currentBrightnessWithinAllowedRange = BrightnessSynchronizer.floatEquals(
968                 mScreenAutoBrightness, clampScreenBrightness(mScreenAutoBrightness));
969         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
970         // in which case we ignore the new screen brightness if it doesn't differ enough from the
971         // previous one.
972         boolean withinThreshold = !Float.isNaN(mScreenAutoBrightness)
973                 && newScreenAutoBrightness > mScreenDarkeningThreshold
974                 && newScreenAutoBrightness < mScreenBrighteningThreshold;
975 
976         if (withinThreshold && !isManuallySet && currentBrightnessWithinAllowedRange) {
977             if (mLoggingEnabled) {
978                 Slog.d(TAG, "ignoring newScreenAutoBrightness: "
979                         + mScreenDarkeningThreshold + " < " + newScreenAutoBrightness
980                         + " < " + mScreenBrighteningThreshold);
981             }
982             return;
983         }
984         if (!BrightnessSynchronizer.floatEquals(mScreenAutoBrightness,
985                 newScreenAutoBrightness)) {
986             if (mLoggingEnabled) {
987                 Slog.d(TAG, "updateAutoBrightness: "
988                         + "mScreenAutoBrightness=" + mScreenAutoBrightness + ", "
989                         + "newScreenAutoBrightness=" + newScreenAutoBrightness);
990             }
991             if (!withinThreshold) {
992                 mPreThresholdBrightness = mScreenAutoBrightness;
993             }
994             mScreenAutoBrightness = newScreenAutoBrightness;
995             if (isInIdleMode()) {
996                 mScreenBrighteningThreshold = clampScreenBrightness(
997                         mScreenBrightnessThresholdsIdle.getBrighteningThreshold(
998                                 newScreenAutoBrightness));
999                 mScreenDarkeningThreshold = clampScreenBrightness(
1000                         mScreenBrightnessThresholdsIdle.getDarkeningThreshold(
1001                                 newScreenAutoBrightness));
1002             } else {
1003                 mScreenBrighteningThreshold = clampScreenBrightness(
1004                         mScreenBrightnessThresholds.getBrighteningThreshold(
1005                                 newScreenAutoBrightness));
1006                 mScreenDarkeningThreshold = clampScreenBrightness(
1007                         mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
1008             }
1009 
1010             if (sendUpdate) {
1011                 mCallbacks.updateBrightness();
1012             }
1013         }
1014     }
1015 
1016     // Clamps values with float range [0.0-1.0]
1017     private float clampScreenBrightness(float value) {
1018         final float minBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMin(),
1019                 mBrightnessThrottler.getBrightnessCap());
1020         final float maxBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMax(),
1021                 mBrightnessThrottler.getBrightnessCap());
1022         return MathUtils.constrain(value, minBrightness, maxBrightness);
1023     }
1024 
1025     private void prepareBrightnessAdjustmentSample() {
1026         if (!mBrightnessAdjustmentSamplePending) {
1027             mBrightnessAdjustmentSamplePending = true;
1028             mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
1029             mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
1030         } else {
1031             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
1032         }
1033 
1034         mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
1035                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
1036     }
1037 
1038     private void cancelBrightnessAdjustmentSample() {
1039         if (mBrightnessAdjustmentSamplePending) {
1040             mBrightnessAdjustmentSamplePending = false;
1041             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
1042         }
1043     }
1044 
1045     private void collectBrightnessAdjustmentSample() {
1046         if (mBrightnessAdjustmentSamplePending) {
1047             mBrightnessAdjustmentSamplePending = false;
1048             if (mAmbientLuxValid && (mScreenAutoBrightness >= PowerManager.BRIGHTNESS_MIN
1049                     || mScreenAutoBrightness == PowerManager.BRIGHTNESS_OFF_FLOAT)) {
1050                 if (mLoggingEnabled) {
1051                     Slog.d(TAG, "Auto-brightness adjustment changed by user: "
1052                             + "lux=" + mAmbientLux + ", "
1053                             + "brightness=" + mScreenAutoBrightness + ", "
1054                             + "ring=" + mAmbientLightRingBuffer);
1055                 }
1056 
1057                 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
1058                         mBrightnessAdjustmentSampleOldLux,
1059                         mBrightnessAdjustmentSampleOldBrightness,
1060                         mAmbientLux,
1061                         mScreenAutoBrightness);
1062             }
1063         }
1064     }
1065 
1066     // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
1067     // foreground app's package name and category and correct the brightness accordingly.
1068     private void registerForegroundAppUpdater() {
1069         try {
1070             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
1071             // This will not get called until the foreground app changes for the first time, so
1072             // call it explicitly to get the current foreground app's info.
1073             updateForegroundApp();
1074         } catch (RemoteException e) {
1075             if (mLoggingEnabled) {
1076                 Slog.e(TAG, "Failed to register foreground app updater: " + e);
1077             }
1078             // Nothing to do.
1079         }
1080     }
1081 
1082     private void unregisterForegroundAppUpdater() {
1083         try {
1084             mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1085         } catch (RemoteException e) {
1086             // Nothing to do.
1087         }
1088         mForegroundAppPackageName = null;
1089         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
1090     }
1091 
1092     // Set the foreground app's package name and category, so brightness can be corrected per app.
1093     private void updateForegroundApp() {
1094         if (mLoggingEnabled) {
1095             Slog.d(TAG, "Attempting to update foreground app");
1096         }
1097         // The ActivityTaskManager's lock tends to get contended, so this is done in a background
1098         // thread and applied via this thread's handler synchronously.
1099         mInjector.getBackgroundThreadHandler().post(new Runnable() {
1100             public void run() {
1101                 try {
1102                     // The foreground app is the top activity of the focused tasks stack.
1103                     final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo();
1104                     if (info == null || info.topActivity == null) {
1105                         return;
1106                     }
1107                     final String packageName = info.topActivity.getPackageName();
1108                     // If the app didn't change, there's nothing to do. Otherwise, we have to
1109                     // update the category and re-apply the brightness correction.
1110                     String currentForegroundAppPackageName = mForegroundAppPackageName;
1111                     if (currentForegroundAppPackageName != null
1112                             && currentForegroundAppPackageName.equals(packageName)) {
1113                         return;
1114                     }
1115                     mPendingForegroundAppPackageName = packageName;
1116                     mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
1117                     try {
1118                         ApplicationInfo app = mPackageManager.getApplicationInfo(packageName,
1119                                 PackageManager.MATCH_ANY_USER);
1120                         mPendingForegroundAppCategory = app.category;
1121                     } catch (PackageManager.NameNotFoundException e) {
1122                         // Nothing to do
1123                     }
1124                     mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
1125                 } catch (RemoteException e) {
1126                     // Nothing to do
1127                 }
1128             }
1129         });
1130     }
1131 
1132     private void updateForegroundAppSync() {
1133         if (mLoggingEnabled) {
1134             Slog.d(TAG, "Updating foreground app: packageName=" + mPendingForegroundAppPackageName
1135                     + ", category=" + mPendingForegroundAppCategory);
1136         }
1137         mForegroundAppPackageName = mPendingForegroundAppPackageName;
1138         mPendingForegroundAppPackageName = null;
1139         mForegroundAppCategory = mPendingForegroundAppCategory;
1140         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
1141         updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
1142     }
1143 
1144     private void switchModeAndShortTermModels(@AutomaticBrightnessMode int mode) {
1145         // Stash short term model
1146         ShortTermModel tempShortTermModel = new ShortTermModel();
1147         tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(),
1148                 mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true);
1149         mHandler.removeMessages(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL);
1150         // Send delayed timeout
1151         mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL,
1152                 mClock.uptimeMillis()
1153                         + mCurrentBrightnessMapper.getShortTermModelTimeout());
1154 
1155         Slog.i(TAG, "mPreviousShortTermModel: " + mPausedShortTermModel);
1156         // new brightness mapper
1157         mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode);
1158 
1159         // if previous stm has been invalidated, and lux has drastically changed, just use
1160         // the new, reset stm.
1161         // if previous stm is still valid then revalidate it
1162         if (mPausedShortTermModel != null) {
1163             if (!mPausedShortTermModel.maybeReset(mAmbientLux)) {
1164                 setScreenBrightnessByUser(mPausedShortTermModel.mAnchor,
1165                         mPausedShortTermModel.mBrightness);
1166             }
1167             mPausedShortTermModel.copyFrom(tempShortTermModel);
1168         }
1169     }
1170 
1171     /**
1172      * Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes
1173      * care of resetting the short term model wherever required
1174      */
1175     public void switchMode(@AutomaticBrightnessMode int mode, boolean sendUpdate) {
1176         if (!mBrightnessMappingStrategyMap.contains(mode)) {
1177             return;
1178         }
1179         if (mCurrentBrightnessMapper.getMode() == mode) {
1180             return;
1181         }
1182         Slog.i(TAG, "Switching to mode " + autoBrightnessModeToString(mode));
1183         if (mode == AUTO_BRIGHTNESS_MODE_IDLE
1184                 || mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE) {
1185             switchModeAndShortTermModels(mode);
1186         } else {
1187             resetShortTermModel();
1188             mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode);
1189         }
1190         if (sendUpdate) {
1191             update();
1192         } else {
1193             updateAutoBrightness(/* sendUpdate= */ false, /* isManuallySet= */ false);
1194         }
1195     }
1196 
1197     float getUserLux() {
1198         return mCurrentBrightnessMapper.getUserLux();
1199     }
1200 
1201     float getUserNits() {
1202         return convertToNits(mCurrentBrightnessMapper.getUserBrightness());
1203     }
1204 
1205     /**
1206      * Convert a brightness float scale value to a nit value. Adjustments, such as RBC, are not
1207      * applied. This is used when storing the brightness in nits for the default display and when
1208      * passing the brightness value to follower displays.
1209      *
1210      * @param brightness The float scale value
1211      * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is
1212      * possible.
1213      */
1214     public float convertToNits(float brightness) {
1215         return mCurrentBrightnessMapper.convertToNits(brightness);
1216     }
1217 
1218     /**
1219      * Convert a brightness float scale value to a nit value. Adjustments, such as RBC are applied.
1220      * This is used when sending the brightness value to
1221      * {@link com.android.server.display.BrightnessTracker}.
1222      *
1223      * @param brightness The float scale value
1224      * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is
1225      * possible.
1226      */
1227     public float convertToAdjustedNits(float brightness) {
1228         return mCurrentBrightnessMapper.convertToAdjustedNits(brightness);
1229     }
1230 
1231     /**
1232      * Convert a brightness nit value to a float scale value. It is assumed that the nit value
1233      * provided does not have adjustments, such as RBC, applied.
1234      *
1235      * @param nits The nit value
1236      * @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no
1237      * conversion is possible.
1238      */
1239     public float getBrightnessFromNits(float nits) {
1240         return mCurrentBrightnessMapper.getBrightnessFromNits(nits);
1241     }
1242 
1243     public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
1244         mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
1245 
1246         // If rbc is turned on, off or there is a change in strength, we want to reset the short
1247         // term model. Since the nits range at which brightness now operates has changed due to
1248         // RBC/strength change, any short term model based on the previous range should be
1249         // invalidated.
1250         resetShortTermModel();
1251 
1252         // When rbc is turned on, we want to accommodate this change in the short term model.
1253         if (applyAdjustment) {
1254             setScreenBrightnessByUser(getAutomaticScreenBrightness());
1255         }
1256     }
1257 
1258     private boolean shouldApplyDozeScaleFactor() {
1259         // Apply the doze scale factor if the display is in doze. We shouldn't rely on the display
1260         // policy here - the screen might turn on while the policy is POLICY_DOZE and in this
1261         // situation, we shouldn't apply the doze scale factor. We also don't apply the doze scale
1262         // factor if we have a designated brightness curve for doze.
1263         return Display.isDozeState(mDisplayState) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
1264     }
1265 
1266     private class ShortTermModel {
1267         // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
1268         // user's adjustment) immediately, but wait for a drastic enough change in the ambient
1269         // light.
1270         // The anchor determines what were the light levels when the user has set their preference,
1271         // and we use a relative threshold to determine when to revert to the OEM curve.
1272         private float mAnchor = INVALID_LUX;
1273         private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
1274         private boolean mIsValid = false;
1275 
1276         private void reset() {
1277             mAnchor = INVALID_LUX;
1278             mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
1279             mIsValid = false;
1280         }
1281 
1282         private void invalidate() {
1283             mIsValid = false;
1284             if (mLoggingEnabled) {
1285                 Slog.d(TAG, "ShortTermModel: invalidate user data");
1286             }
1287         }
1288 
1289         private void setUserBrightness(float lux, float brightness) {
1290             mAnchor = lux;
1291             mBrightness = brightness;
1292             mIsValid = true;
1293             if (mLoggingEnabled) {
1294                 Slog.d(TAG, "ShortTermModel: anchor=" + mAnchor);
1295             }
1296         }
1297 
1298         private boolean maybeReset(float currentLux) {
1299             // If the short term model was invalidated and the change is drastic enough, reset it.
1300             // Otherwise, we revalidate it.
1301             if (!mIsValid && mAnchor != INVALID_LUX) {
1302                 if (mCurrentBrightnessMapper.shouldResetShortTermModel(currentLux, mAnchor)) {
1303                     resetShortTermModel();
1304                 } else {
1305                     mIsValid = true;
1306                 }
1307                 return mIsValid;
1308             }
1309             return false;
1310         }
1311 
1312         private void set(float anchor, float brightness, boolean valid) {
1313             mAnchor = anchor;
1314             mBrightness = brightness;
1315             mIsValid = valid;
1316         }
1317         private void copyFrom(ShortTermModel from) {
1318             mAnchor = from.mAnchor;
1319             mBrightness = from.mBrightness;
1320             mIsValid = from.mIsValid;
1321         }
1322 
1323         public String toString() {
1324             return "mAnchor: " + mAnchor
1325                     + "\n mBrightness: " + mBrightness
1326                     + "\n mIsValid: " + mIsValid;
1327         }
1328 
1329         void dump(PrintWriter pw) {
1330             pw.println(this);
1331         }
1332 
1333     }
1334 
1335     private final class AutomaticBrightnessHandler extends Handler {
1336         public AutomaticBrightnessHandler(Looper looper) {
1337             super(looper, null, true /*async*/);
1338         }
1339 
1340         @Override
1341         public void handleMessage(Message msg) {
1342             switch (msg.what) {
1343                 case MSG_RUN_UPDATE:
1344                     updateAutoBrightness(true /*sendUpdate*/, false /*isManuallySet*/);
1345                     break;
1346 
1347                 case MSG_UPDATE_AMBIENT_LUX:
1348                     updateAmbientLux();
1349                     break;
1350 
1351                 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
1352                     collectBrightnessAdjustmentSample();
1353                     break;
1354 
1355                 case MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL:
1356                     mShortTermModel.invalidate();
1357                     break;
1358 
1359                 case MSG_UPDATE_FOREGROUND_APP:
1360                     updateForegroundApp();
1361                     break;
1362 
1363                 case MSG_UPDATE_FOREGROUND_APP_SYNC:
1364                     updateForegroundAppSync();
1365                     break;
1366 
1367                 case MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL:
1368                     mPausedShortTermModel.invalidate();
1369                     break;
1370             }
1371         }
1372     }
1373 
1374     private final SensorEventListener mLightSensorListener = new SensorEventListener() {
1375         @Override
1376         public void onSensorChanged(SensorEvent event) {
1377             if (mLightSensorEnabled) {
1378                 // The time received from the sensor is in nano seconds, hence changing it to ms
1379                 final long time = (mDisplayManagerFlags.offloadControlsDozeAutoBrightness())
1380                         ? TimeUnit.NANOSECONDS.toMillis(event.timestamp) : mClock.uptimeMillis();
1381                 final float lux = event.values[0];
1382                 handleLightSensorEvent(time, lux);
1383             }
1384         }
1385 
1386         @Override
1387         public void onAccuracyChanged(Sensor sensor, int accuracy) {
1388             // Not used.
1389         }
1390     };
1391 
1392     // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
1393     // moving to top.
1394     class TaskStackListenerImpl extends TaskStackListener {
1395         @Override
onTaskStackChanged()1396         public void onTaskStackChanged() {
1397             mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
1398         }
1399     }
1400 
1401     /** Callbacks to request updates to the display's power state. */
1402     interface Callbacks {
1403         void updateBrightness();
1404     }
1405 
1406     /** Functional interface for providing time. */
1407     @VisibleForTesting
1408     interface Clock {
1409         /**
1410          * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
1411          */
1412         long uptimeMillis();
1413 
1414         /**
1415          * Gets the time on either the elapsedTime or the uptime scale, depending on how we
1416          * processing the events from the sensor
1417          */
1418         long getSensorEventScaleTime();
1419     }
1420 
1421     /**
1422      * A ring buffer of ambient light measurements sorted by time.
1423      *
1424      * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
1425      * from oldest to newest.
1426      */
1427     private static final class AmbientLightRingBuffer {
1428         // Proportional extra capacity of the buffer beyond the expected number of light samples
1429         // in the horizon
1430         private static final float BUFFER_SLACK = 1.5f;
1431         private float[] mRingLux;
1432         private long[] mRingTime;
1433         private int mCapacity;
1434 
1435         // The first valid element and the next open slot.
1436         // Note that if mCount is zero then there are no valid elements.
1437         private int mStart;
1438         private int mEnd;
1439         private int mCount;
1440         Clock mClock;
1441 
AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock)1442         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock) {
1443             if (lightSensorRate <= 0) {
1444                 throw new IllegalArgumentException("lightSensorRate must be above 0");
1445             }
1446             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
1447             mRingLux = new float[mCapacity];
1448             mRingTime = new long[mCapacity];
1449             mClock = clock;
1450         }
1451 
getLux(int index)1452         public float getLux(int index) {
1453             return mRingLux[offsetOf(index)];
1454         }
1455 
getAllLuxValues()1456         public float[] getAllLuxValues() {
1457             float[] values = new float[mCount];
1458             if (mCount == 0) {
1459                 return values;
1460             }
1461 
1462             if (mStart < mEnd) {
1463                 System.arraycopy(mRingLux, mStart, values, 0, mCount);
1464             } else {
1465                 System.arraycopy(mRingLux, mStart, values, 0, mCapacity - mStart);
1466                 System.arraycopy(mRingLux, 0, values, mCapacity - mStart, mEnd);
1467             }
1468 
1469             return values;
1470         }
1471 
getTime(int index)1472         public long getTime(int index) {
1473             return mRingTime[offsetOf(index)];
1474         }
1475 
getAllTimestamps()1476         public long[] getAllTimestamps() {
1477             long[] values = new long[mCount];
1478             if (mCount == 0) {
1479                 return values;
1480             }
1481 
1482             if (mStart < mEnd) {
1483                 System.arraycopy(mRingTime, mStart, values, 0, mCount);
1484             } else {
1485                 System.arraycopy(mRingTime, mStart, values, 0, mCapacity - mStart);
1486                 System.arraycopy(mRingTime, 0, values, mCapacity - mStart, mEnd);
1487             }
1488 
1489             return values;
1490         }
1491 
push(long time, float lux)1492         public void push(long time, float lux) {
1493             int next = mEnd;
1494             if (mCount == mCapacity) {
1495                 int newSize = mCapacity * 2;
1496 
1497                 float[] newRingLux = new float[newSize];
1498                 long[] newRingTime = new long[newSize];
1499                 int length = mCapacity - mStart;
1500                 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
1501                 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
1502                 if (mStart != 0) {
1503                     System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
1504                     System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
1505                 }
1506                 mRingLux = newRingLux;
1507                 mRingTime = newRingTime;
1508 
1509                 next = mCapacity;
1510                 mCapacity = newSize;
1511                 mStart = 0;
1512             }
1513             mRingTime[next] = time;
1514             mRingLux[next] = lux;
1515             mEnd = next + 1;
1516             if (mEnd == mCapacity) {
1517                 mEnd = 0;
1518             }
1519             mCount++;
1520         }
1521 
prune(long horizon)1522         public void prune(long horizon) {
1523             if (mCount == 0) {
1524                 return;
1525             }
1526 
1527             while (mCount > 1) {
1528                 int next = mStart + 1;
1529                 if (next >= mCapacity) {
1530                     next -= mCapacity;
1531                 }
1532                 if (mRingTime[next] > horizon) {
1533                     // Some light sensors only produce data upon a change in the ambient light
1534                     // levels, so we need to consider the previous measurement as the ambient light
1535                     // level for all points in time up until we receive a new measurement. Thus, we
1536                     // always want to keep the youngest element that would be removed from the
1537                     // buffer and just set its measurement time to the horizon time since at that
1538                     // point it is the ambient light level, and to remove it would be to drop a
1539                     // valid data point within our horizon.
1540                     break;
1541                 }
1542                 mStart = next;
1543                 mCount -= 1;
1544             }
1545 
1546             if (mRingTime[mStart] < horizon) {
1547                 mRingTime[mStart] = horizon;
1548             }
1549         }
1550 
size()1551         public int size() {
1552             return mCount;
1553         }
1554 
clear()1555         public void clear() {
1556             mStart = 0;
1557             mEnd = 0;
1558             mCount = 0;
1559         }
1560 
1561         @Override
toString()1562         public String toString() {
1563             StringBuilder buf = new StringBuilder();
1564             buf.append('[');
1565             for (int i = 0; i < mCount; i++) {
1566                 final long next = i + 1 < mCount ? getTime(i + 1)
1567                         : mClock.getSensorEventScaleTime();
1568                 if (i != 0) {
1569                     buf.append(", ");
1570                 }
1571                 buf.append(getLux(i));
1572                 buf.append(" / ");
1573                 buf.append(next - getTime(i));
1574                 buf.append("ms");
1575             }
1576             buf.append(']');
1577             return buf.toString();
1578         }
1579 
1580         private int offsetOf(int index) {
1581             if (index >= mCount || index < 0) {
1582                 throw new ArrayIndexOutOfBoundsException(index);
1583             }
1584             index += mStart;
1585             if (index >= mCapacity) {
1586                 index -= mCapacity;
1587             }
1588             return index;
1589         }
1590     }
1591 
1592     private static class RealClock implements Clock {
1593         private final boolean mOffloadControlsDozeBrightness;
1594 
RealClock(boolean offloadControlsDozeBrightness)1595         RealClock(boolean offloadControlsDozeBrightness) {
1596             mOffloadControlsDozeBrightness = offloadControlsDozeBrightness;
1597         }
1598 
1599         @Override
uptimeMillis()1600         public long uptimeMillis() {
1601             return SystemClock.uptimeMillis();
1602         }
1603 
getSensorEventScaleTime()1604         public long getSensorEventScaleTime() {
1605             return (mOffloadControlsDozeBrightness)
1606                     ? SystemClock.elapsedRealtime() : uptimeMillis();
1607         }
1608     }
1609 
1610     public static class Injector {
getBackgroundThreadHandler()1611         public Handler getBackgroundThreadHandler() {
1612             return BackgroundThread.getHandler();
1613         }
1614 
createClock(boolean offloadControlsDozeBrightness)1615         Clock createClock(boolean offloadControlsDozeBrightness) {
1616             return new RealClock(offloadControlsDozeBrightness);
1617         }
1618     }
1619 }
1620