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