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 android.annotation.Nullable;
20 import android.app.ActivityTaskManager;
21 import android.app.ActivityTaskManager.RootTaskInfo;
22 import android.app.IActivityTaskManager;
23 import android.app.TaskStackListener;
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.hardware.Sensor;
28 import android.hardware.SensorEvent;
29 import android.hardware.SensorEventListener;
30 import android.hardware.SensorManager;
31 import android.hardware.display.BrightnessConfiguration;
32 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
33 import android.os.Handler;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.os.PowerManager;
37 import android.os.RemoteException;
38 import android.os.SystemClock;
39 import android.os.Trace;
40 import android.util.EventLog;
41 import android.util.MathUtils;
42 import android.util.Slog;
43 import android.util.TimeUtils;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.display.BrightnessSynchronizer;
47 import com.android.internal.os.BackgroundThread;
48 import com.android.server.EventLogTags;
49 
50 import java.io.PrintWriter;
51 
52 class AutomaticBrightnessController {
53     private static final String TAG = "AutomaticBrightnessController";
54 
55     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
56 
57     // How long the current sensor reading is assumed to be valid beyond the current time.
58     // This provides a bit of prediction, as well as ensures that the weight for the last sample is
59     // non-zero, which in turn ensures that the total weight is non-zero.
60     private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
61 
62     // Debounce for sampling user-initiated changes in display brightness to ensure
63     // the user is satisfied with the result before storing the sample.
64     private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
65 
66     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
67     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
68     private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
69     private static final int MSG_UPDATE_FOREGROUND_APP = 4;
70     private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
71     private static final int MSG_RUN_UPDATE = 6;
72 
73     // Length of the ambient light horizon used to calculate the long term estimate of ambient
74     // light.
75     private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
76 
77     // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
78     private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
79 
80     // Callbacks for requesting updates to the display's power state
81     private final Callbacks mCallbacks;
82 
83     // The sensor manager.
84     private final SensorManager mSensorManager;
85 
86     // The light sensor, or null if not available or needed.
87     private final Sensor mLightSensor;
88 
89     // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
90     private final BrightnessMappingStrategy mBrightnessMapper;
91 
92     // The minimum and maximum screen brightnesses.
93     private final float mScreenBrightnessRangeMinimum;
94     private final float mScreenBrightnessRangeMaximum;
95 
96     // How much to scale doze brightness by (should be (0, 1.0]).
97     private final float mDozeScaleFactor;
98 
99     // Initial light sensor event rate in milliseconds.
100     private final int mInitialLightSensorRate;
101 
102     // Steady-state light sensor event rate in milliseconds.
103     private final int mNormalLightSensorRate;
104 
105     // The current light sensor event rate in milliseconds.
106     private int mCurrentLightSensorRate;
107 
108     // Stability requirements in milliseconds for accepting a new brightness level.  This is used
109     // for debouncing the light sensor.  Different constants are used to debounce the light sensor
110     // when adapting to brighter or darker environments.  This parameter controls how quickly
111     // brightness changes occur in response to an observed change in light level that exceeds the
112     // hysteresis threshold.
113     private final long mBrighteningLightDebounceConfig;
114     private final long mDarkeningLightDebounceConfig;
115 
116     // If true immediately after the screen is turned on the controller will try to adjust the
117     // brightness based on the current sensor reads. If false, the controller will collect more data
118     // and only then decide whether to change brightness.
119     private final boolean mResetAmbientLuxAfterWarmUpConfig;
120 
121     // Period of time in which to consider light samples in milliseconds.
122     private final int mAmbientLightHorizon;
123 
124     // The intercept used for the weighting calculation. This is used in order to keep all possible
125     // weighting values positive.
126     private final int mWeightingIntercept;
127 
128     // Configuration object for determining thresholds to change brightness dynamically
129     private final HysteresisLevels mAmbientBrightnessThresholds;
130     private final HysteresisLevels mScreenBrightnessThresholds;
131 
132     private boolean mLoggingEnabled;
133 
134     // Amount of time to delay auto-brightness after screen on while waiting for
135     // the light sensor to warm-up in milliseconds.
136     // May be 0 if no warm-up is required.
137     private int mLightSensorWarmUpTimeConfig;
138 
139     // Set to true if the light sensor is enabled.
140     private boolean mLightSensorEnabled;
141 
142     // The time when the light sensor was enabled.
143     private long mLightSensorEnableTime;
144 
145     // The currently accepted nominal ambient light level.
146     private float mAmbientLux;
147 
148     // True if mAmbientLux holds a valid value.
149     private boolean mAmbientLuxValid;
150 
151     // The ambient light level threshold at which to brighten or darken the screen.
152     private float mAmbientBrighteningThreshold;
153     private float mAmbientDarkeningThreshold;
154 
155     // The screen brightness threshold at which to brighten or darken the screen.
156     private float mScreenBrighteningThreshold;
157     private float mScreenDarkeningThreshold;
158     // The most recent light sample.
159     private float mLastObservedLux;
160 
161     // The time of the most light recent sample.
162     private long mLastObservedLuxTime;
163 
164     // The number of light samples collected since the light sensor was enabled.
165     private int mRecentLightSamples;
166 
167     // A ring buffer containing all of the recent ambient light sensor readings.
168     private AmbientLightRingBuffer mAmbientLightRingBuffer;
169 
170     // The handler
171     private AutomaticBrightnessHandler mHandler;
172 
173     // The screen brightness level that has been chosen by the auto-brightness
174     // algorithm.  The actual brightness should ramp towards this value.
175     // We preserve this value even when we stop using the light sensor so
176     // that we can quickly revert to the previous auto-brightness level
177     // while the light sensor warms up.
178     // Use PowerManager.BRIGHTNESS_INVALID_FLOAT if there is no current auto-brightness value
179     // available.
180     private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
181 
182     // The current display policy. This is useful, for example,  for knowing when we're dozing,
183     // where the light sensor may not be available.
184     private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
185 
186     // True if we are collecting a brightness adjustment sample, along with some data
187     // for the initial state of the sample.
188     private boolean mBrightnessAdjustmentSamplePending;
189     private float mBrightnessAdjustmentSampleOldLux;
190     private float mBrightnessAdjustmentSampleOldBrightness;
191 
192     // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
193     // user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
194     // The anchor determines what were the light levels when the user has set their preference, and
195     // we use a relative threshold to determine when to revert to the OEM curve.
196     private boolean mShortTermModelValid;
197     private float mShortTermModelAnchor;
198 
199     // Controls High Brightness Mode.
200     private HighBrightnessModeController mHbmController;
201 
202     // Context-sensitive brightness configurations require keeping track of the foreground app's
203     // package name and category, which is done by registering a TaskStackListener to call back to
204     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
205     // package name and PackageManager to get its category (so might as well cache them).
206     private String mForegroundAppPackageName;
207     private String mPendingForegroundAppPackageName;
208     private @ApplicationInfo.Category int mForegroundAppCategory;
209     private @ApplicationInfo.Category int mPendingForegroundAppCategory;
210     private TaskStackListenerImpl mTaskStackListener;
211     private IActivityTaskManager mActivityTaskManager;
212     private PackageManager mPackageManager;
213     private Context mContext;
214 
215     private final Injector mInjector;
216 
AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context, HighBrightnessModeController hbmController)217     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
218             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
219             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
220             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
221             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
222             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
223             HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
224             HighBrightnessModeController hbmController) {
225         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
226                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
227                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
228                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
229                 ambientBrightnessThresholds, screenBrightnessThresholds, display, context,
230                 hbmController
231         );
232     }
233 
234     @VisibleForTesting
AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context, HighBrightnessModeController hbmController)235     AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
236             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
237             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
238             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
239             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
240             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
241             HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
242             HighBrightnessModeController hbmController) {
243         mInjector = injector;
244         mContext = context;
245         mCallbacks = callbacks;
246         mSensorManager = sensorManager;
247         mBrightnessMapper = mapper;
248         mScreenBrightnessRangeMinimum = brightnessMin;
249         mScreenBrightnessRangeMaximum = brightnessMax;
250         mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
251         mDozeScaleFactor = dozeScaleFactor;
252         mNormalLightSensorRate = lightSensorRate;
253         mInitialLightSensorRate = initialLightSensorRate;
254         mCurrentLightSensorRate = -1;
255         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
256         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
257         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
258         mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
259         mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
260         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
261         mScreenBrightnessThresholds = screenBrightnessThresholds;
262         mShortTermModelValid = true;
263         mShortTermModelAnchor = -1;
264         mHandler = new AutomaticBrightnessHandler(looper);
265         mAmbientLightRingBuffer =
266             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
267 
268         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
269             mLightSensor = lightSensor;
270         }
271 
272         mActivityTaskManager = ActivityTaskManager.getService();
273         mPackageManager = mContext.getPackageManager();
274         mTaskStackListener = new TaskStackListenerImpl();
275         mForegroundAppPackageName = null;
276         mPendingForegroundAppPackageName = null;
277         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
278         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
279         mHbmController = hbmController;
280     }
281 
282     /**
283      * Enable/disable logging.
284      *
285      * @param loggingEnabled
286      *      Whether logging should be on/off.
287      *
288      * @return Whether the method succeeded or not.
289      */
setLoggingEnabled(boolean loggingEnabled)290     public boolean setLoggingEnabled(boolean loggingEnabled) {
291         if (mLoggingEnabled == loggingEnabled) {
292             return false;
293         }
294         mBrightnessMapper.setLoggingEnabled(loggingEnabled);
295         mLoggingEnabled = loggingEnabled;
296         return true;
297     }
298 
getAutomaticScreenBrightness()299     public float getAutomaticScreenBrightness() {
300         if (!mAmbientLuxValid) {
301             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
302         }
303         if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
304             return mScreenAutoBrightness * mDozeScaleFactor;
305         }
306         return mScreenAutoBrightness;
307     }
308 
hasValidAmbientLux()309     public boolean hasValidAmbientLux() {
310         return mAmbientLuxValid;
311     }
312 
getAutomaticScreenBrightnessAdjustment()313     public float getAutomaticScreenBrightnessAdjustment() {
314         return mBrightnessMapper.getAutoBrightnessAdjustment();
315     }
316 
configure(boolean enable, @Nullable BrightnessConfiguration configuration, float brightness, boolean userChangedBrightness, float adjustment, boolean userChangedAutoBrightnessAdjustment, int displayPolicy)317     public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
318             float brightness, boolean userChangedBrightness, float adjustment,
319             boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
320         mHbmController.setAutoBrightnessEnabled(enable);
321         // While dozing, the application processor may be suspended which will prevent us from
322         // receiving new information from the light sensor. On some devices, we may be able to
323         // switch to a wake-up light sensor instead but for now we will simply disable the sensor
324         // and hold onto the last computed screen auto brightness.  We save the dozing flag for
325         // debugging purposes.
326         boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
327         boolean changed = setBrightnessConfiguration(configuration);
328         changed |= setDisplayPolicy(displayPolicy);
329         if (userChangedAutoBrightnessAdjustment) {
330             changed |= setAutoBrightnessAdjustment(adjustment);
331         }
332         if (userChangedBrightness && enable) {
333             // Update the brightness curve with the new user control point. It's critical this
334             // happens after we update the autobrightness adjustment since it may reset it.
335             changed |= setScreenBrightnessByUser(brightness);
336         }
337         final boolean userInitiatedChange =
338                 userChangedBrightness || userChangedAutoBrightnessAdjustment;
339         if (userInitiatedChange && enable && !dozing) {
340             prepareBrightnessAdjustmentSample();
341         }
342         changed |= setLightSensorEnabled(enable && !dozing);
343         if (changed) {
344             updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange);
345         }
346     }
347 
stop()348     public void stop() {
349         setLightSensorEnabled(false);
350     }
351 
hasUserDataPoints()352     public boolean hasUserDataPoints() {
353         return mBrightnessMapper.hasUserDataPoints();
354     }
355 
isDefaultConfig()356     public boolean isDefaultConfig() {
357         return mBrightnessMapper.isDefaultConfig();
358     }
359 
getDefaultConfig()360     public BrightnessConfiguration getDefaultConfig() {
361         return mBrightnessMapper.getDefaultConfig();
362     }
363 
364     /**
365      * Force recalculate of the state of automatic brightness.
366      */
update()367     public void update() {
368         mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
369     }
370 
setDisplayPolicy(int policy)371     private boolean setDisplayPolicy(int policy) {
372         if (mDisplayPolicy == policy) {
373             return false;
374         }
375         final int oldPolicy = mDisplayPolicy;
376         mDisplayPolicy = policy;
377         if (mLoggingEnabled) {
378             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
379         }
380         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
381             mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
382                     mBrightnessMapper.getShortTermModelTimeout());
383         } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
384             mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
385         }
386         return true;
387     }
388 
isInteractivePolicy(int policy)389     private static boolean isInteractivePolicy(int policy) {
390         return policy == DisplayPowerRequest.POLICY_BRIGHT
391                 || policy == DisplayPowerRequest.POLICY_DIM
392                 || policy == DisplayPowerRequest.POLICY_VR;
393     }
394 
setScreenBrightnessByUser(float brightness)395     private boolean setScreenBrightnessByUser(float brightness) {
396         if (!mAmbientLuxValid) {
397             // If we don't have a valid ambient lux then we don't have a valid brightness anyway,
398             // and we can't use this data to add a new control point to the short-term model.
399             return false;
400         }
401         mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
402         mShortTermModelValid = true;
403         mShortTermModelAnchor = mAmbientLux;
404         if (mLoggingEnabled) {
405             Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
406         }
407         return true;
408     }
409 
resetShortTermModel()410     public void resetShortTermModel() {
411         mBrightnessMapper.clearUserDataPoints();
412         mShortTermModelValid = true;
413         mShortTermModelAnchor = -1;
414     }
415 
invalidateShortTermModel()416     private void invalidateShortTermModel() {
417         if (mLoggingEnabled) {
418             Slog.d(TAG, "ShortTermModel: invalidate user data");
419         }
420         mShortTermModelValid = false;
421     }
422 
setBrightnessConfiguration(BrightnessConfiguration configuration)423     public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
424         if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
425             resetShortTermModel();
426             return true;
427         }
428         return false;
429     }
430 
dump(PrintWriter pw)431     public void dump(PrintWriter pw) {
432         pw.println();
433         pw.println("Automatic Brightness Controller Configuration:");
434         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
435         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
436         pw.println("  mDozeScaleFactor=" + mDozeScaleFactor);
437         pw.println("  mInitialLightSensorRate=" + mInitialLightSensorRate);
438         pw.println("  mNormalLightSensorRate=" + mNormalLightSensorRate);
439         pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
440         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
441         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
442         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
443         pw.println("  mAmbientLightHorizon=" + mAmbientLightHorizon);
444         pw.println("  mWeightingIntercept=" + mWeightingIntercept);
445 
446         pw.println();
447         pw.println("Automatic Brightness Controller State:");
448         pw.println("  mLightSensor=" + mLightSensor);
449         pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
450         pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
451         pw.println("  mCurrentLightSensorRate=" + mCurrentLightSensorRate);
452         pw.println("  mAmbientLux=" + mAmbientLux);
453         pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
454         pw.println("  mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
455         pw.println("  mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
456         pw.println("  mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
457         pw.println("  mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
458         pw.println("  mLastObservedLux=" + mLastObservedLux);
459         pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
460         pw.println("  mRecentLightSamples=" + mRecentLightSamples);
461         pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
462         pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
463         pw.println("  mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
464         pw.println("  mShortTermModelTimeout=" + mBrightnessMapper.getShortTermModelTimeout());
465         pw.println("  mShortTermModelAnchor=" + mShortTermModelAnchor);
466         pw.println("  mShortTermModelValid=" + mShortTermModelValid);
467         pw.println("  mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
468         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
469         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
470                 + mBrightnessAdjustmentSampleOldBrightness);
471         pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
472         pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
473         pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
474         pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
475 
476         pw.println();
477         mBrightnessMapper.dump(pw);
478 
479         pw.println();
480         mAmbientBrightnessThresholds.dump(pw);
481         mScreenBrightnessThresholds.dump(pw);
482     }
483 
setLightSensorEnabled(boolean enable)484     private boolean setLightSensorEnabled(boolean enable) {
485         if (enable) {
486             if (!mLightSensorEnabled) {
487                 mLightSensorEnabled = true;
488                 mLightSensorEnableTime = SystemClock.uptimeMillis();
489                 mCurrentLightSensorRate = mInitialLightSensorRate;
490                 registerForegroundAppUpdater();
491                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
492                         mCurrentLightSensorRate * 1000, mHandler);
493                 return true;
494             }
495         } else if (mLightSensorEnabled) {
496             mLightSensorEnabled = false;
497             mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
498             mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
499             mRecentLightSamples = 0;
500             mAmbientLightRingBuffer.clear();
501             mCurrentLightSensorRate = -1;
502             mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
503             unregisterForegroundAppUpdater();
504             mSensorManager.unregisterListener(mLightSensorListener);
505         }
506         return false;
507     }
508 
handleLightSensorEvent(long time, float lux)509     private void handleLightSensorEvent(long time, float lux) {
510         Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
511         mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
512 
513         if (mAmbientLightRingBuffer.size() == 0) {
514             // switch to using the steady-state sample rate after grabbing the initial light sample
515             adjustLightSensorRate(mNormalLightSensorRate);
516         }
517         applyLightSensorMeasurement(time, lux);
518         updateAmbientLux(time);
519     }
520 
applyLightSensorMeasurement(long time, float lux)521     private void applyLightSensorMeasurement(long time, float lux) {
522         mRecentLightSamples++;
523         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
524         mAmbientLightRingBuffer.push(time, lux);
525 
526         // Remember this sample value.
527         mLastObservedLux = lux;
528         mLastObservedLuxTime = time;
529     }
530 
adjustLightSensorRate(int lightSensorRate)531     private void adjustLightSensorRate(int lightSensorRate) {
532         // if the light sensor rate changed, update the sensor listener
533         if (lightSensorRate != mCurrentLightSensorRate) {
534             if (mLoggingEnabled) {
535                 Slog.d(TAG, "adjustLightSensorRate: " +
536                         "previousRate=" + mCurrentLightSensorRate + ", " +
537                         "currentRate=" + lightSensorRate);
538             }
539             mCurrentLightSensorRate = lightSensorRate;
540             mSensorManager.unregisterListener(mLightSensorListener);
541             mSensorManager.registerListener(mLightSensorListener, mLightSensor,
542                     lightSensorRate * 1000, mHandler);
543         }
544     }
545 
setAutoBrightnessAdjustment(float adjustment)546     private boolean setAutoBrightnessAdjustment(float adjustment) {
547         return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
548     }
549 
setAmbientLux(float lux)550     private void setAmbientLux(float lux) {
551         if (mLoggingEnabled) {
552             Slog.d(TAG, "setAmbientLux(" + lux + ")");
553         }
554         if (lux < 0) {
555             Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
556             lux = 0;
557         }
558         mAmbientLux = lux;
559         mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
560         mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
561         mHbmController.onAmbientLuxChange(mAmbientLux);
562 
563         // If the short term model was invalidated and the change is drastic enough, reset it.
564         if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
565             if (mBrightnessMapper.shouldResetShortTermModel(mAmbientLux, mShortTermModelAnchor)) {
566                 resetShortTermModel();
567             } else {
568                 mShortTermModelValid = true;
569             }
570         }
571     }
572 
calculateAmbientLux(long now, long horizon)573     private float calculateAmbientLux(long now, long horizon) {
574         if (mLoggingEnabled) {
575             Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
576         }
577         final int N = mAmbientLightRingBuffer.size();
578         if (N == 0) {
579             Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
580             return -1;
581         }
582 
583         // Find the first measurement that is just outside of the horizon.
584         int endIndex = 0;
585         final long horizonStartTime = now - horizon;
586         for (int i = 0; i < N-1; i++) {
587             if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
588                 endIndex++;
589             } else {
590                 break;
591             }
592         }
593         if (mLoggingEnabled) {
594             Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
595                     mAmbientLightRingBuffer.getTime(endIndex) + ", " +
596                     mAmbientLightRingBuffer.getLux(endIndex) + ")");
597         }
598         float sum = 0;
599         float totalWeight = 0;
600         long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
601         for (int i = N - 1; i >= endIndex; i--) {
602             long eventTime = mAmbientLightRingBuffer.getTime(i);
603             if (i == endIndex && eventTime < horizonStartTime) {
604                 // If we're at the final value, make sure we only consider the part of the sample
605                 // within our desired horizon.
606                 eventTime = horizonStartTime;
607             }
608             final long startTime = eventTime - now;
609             float weight = calculateWeight(startTime, endTime);
610             float lux = mAmbientLightRingBuffer.getLux(i);
611             if (mLoggingEnabled) {
612                 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
613                         "lux=" + lux + ", " +
614                         "weight=" + weight);
615             }
616             totalWeight += weight;
617             sum += lux * weight;
618             endTime = startTime;
619         }
620         if (mLoggingEnabled) {
621             Slog.d(TAG, "calculateAmbientLux: " +
622                     "totalWeight=" + totalWeight + ", " +
623                     "newAmbientLux=" + (sum / totalWeight));
624         }
625         return sum / totalWeight;
626     }
627 
calculateWeight(long startDelta, long endDelta)628     private float calculateWeight(long startDelta, long endDelta) {
629         return weightIntegral(endDelta) - weightIntegral(startDelta);
630     }
631 
632     // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
633     // horizon we're looking at and provides a non-linear weighting for light samples.
weightIntegral(long x)634     private float weightIntegral(long x) {
635         return x * (x * 0.5f + mWeightingIntercept);
636     }
637 
nextAmbientLightBrighteningTransition(long time)638     private long nextAmbientLightBrighteningTransition(long time) {
639         final int N = mAmbientLightRingBuffer.size();
640         long earliestValidTime = time;
641         for (int i = N - 1; i >= 0; i--) {
642             if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
643                 break;
644             }
645             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
646         }
647         return earliestValidTime + mBrighteningLightDebounceConfig;
648     }
649 
nextAmbientLightDarkeningTransition(long time)650     private long nextAmbientLightDarkeningTransition(long time) {
651         final int N = mAmbientLightRingBuffer.size();
652         long earliestValidTime = time;
653         for (int i = N - 1; i >= 0; i--) {
654             if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
655                 break;
656             }
657             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
658         }
659         return earliestValidTime + mDarkeningLightDebounceConfig;
660     }
661 
updateAmbientLux()662     private void updateAmbientLux() {
663         long time = SystemClock.uptimeMillis();
664         mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
665         updateAmbientLux(time);
666     }
667 
updateAmbientLux(long time)668     private void updateAmbientLux(long time) {
669         // If the light sensor was just turned on then immediately update our initial
670         // estimate of the current ambient light level.
671         if (!mAmbientLuxValid) {
672             final long timeWhenSensorWarmedUp =
673                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
674             if (time < timeWhenSensorWarmedUp) {
675                 if (mLoggingEnabled) {
676                     Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
677                             + "time=" + time + ", "
678                             + "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
679                 }
680                 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
681                         timeWhenSensorWarmedUp);
682                 return;
683             }
684             setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
685             mAmbientLuxValid = true;
686             if (mLoggingEnabled) {
687                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
688                         "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
689                         "mAmbientLux=" + mAmbientLux);
690             }
691             updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
692         }
693 
694         long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
695         long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
696         // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
697         // change in lighting conditions, and a fast ambient lux to determine what the new
698         // brightness situation is since the slow lux can be quite slow to converge.
699         //
700         // Note that both values need to be checked for sufficient change before updating the
701         // proposed ambient light value since the slow value might be sufficiently far enough away
702         // from the fast value to cause a recalculation while its actually just converging on
703         // the fast value still.
704         float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
705         float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
706 
707         if ((slowAmbientLux >= mAmbientBrighteningThreshold
708                 && fastAmbientLux >= mAmbientBrighteningThreshold
709                 && nextBrightenTransition <= time)
710                 || (slowAmbientLux <= mAmbientDarkeningThreshold
711                         && fastAmbientLux <= mAmbientDarkeningThreshold
712                         && nextDarkenTransition <= time)) {
713             setAmbientLux(fastAmbientLux);
714             if (mLoggingEnabled) {
715                 Slog.d(TAG, "updateAmbientLux: "
716                         + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
717                         + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
718                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
719                         + "mAmbientLux=" + mAmbientLux);
720             }
721             updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
722             nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
723             nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
724         }
725         long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
726         // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
727         // exceed the necessary threshold, then it's possible we'll get a transition time prior to
728         // now. Rather than continually checking to see whether the weighted lux exceeds the
729         // threshold, schedule an update for when we'd normally expect another light sample, which
730         // should be enough time to decide whether we should actually transition to the new
731         // weighted ambient lux or not.
732         nextTransitionTime =
733                 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
734         if (mLoggingEnabled) {
735             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
736                     nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
737         }
738         mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
739     }
740 
updateAutoBrightness(boolean sendUpdate, boolean isManuallySet)741     private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
742         if (!mAmbientLuxValid) {
743             return;
744         }
745 
746         float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
747                 mForegroundAppCategory);
748         float newScreenAutoBrightness = clampScreenBrightness(value);
749 
750         // The min/max range can change for brightness due to HBM. See if the current brightness
751         // value still falls within the current range (which could have changed).
752         final boolean currentBrightnessWithinAllowedRange = BrightnessSynchronizer.floatEquals(
753                 mScreenAutoBrightness, clampScreenBrightness(mScreenAutoBrightness));
754         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
755         // in which case we ignore the new screen brightness if it doesn't differ enough from the
756         // previous one.
757         if (!Float.isNaN(mScreenAutoBrightness)
758                 && !isManuallySet
759                 && newScreenAutoBrightness > mScreenDarkeningThreshold
760                 && newScreenAutoBrightness < mScreenBrighteningThreshold
761                 && currentBrightnessWithinAllowedRange) {
762             if (mLoggingEnabled) {
763                 Slog.d(TAG, "ignoring newScreenAutoBrightness: "
764                         + mScreenDarkeningThreshold + " < " + newScreenAutoBrightness
765                         + " < " + mScreenBrighteningThreshold);
766             }
767             return;
768         }
769         if (!BrightnessSynchronizer.floatEquals(mScreenAutoBrightness,
770                 newScreenAutoBrightness)) {
771             if (mLoggingEnabled) {
772                 Slog.d(TAG, "updateAutoBrightness: "
773                         + "mScreenAutoBrightness=" + mScreenAutoBrightness + ", "
774                         + "newScreenAutoBrightness=" + newScreenAutoBrightness);
775             }
776             mScreenAutoBrightness = newScreenAutoBrightness;
777             mScreenBrighteningThreshold = clampScreenBrightness(
778                     mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
779             mScreenDarkeningThreshold = clampScreenBrightness(
780                     mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
781 
782             if (sendUpdate) {
783                 mCallbacks.updateBrightness();
784             }
785         }
786     }
787 
788     // Clamps values with float range [0.0-1.0]
clampScreenBrightness(float value)789     private float clampScreenBrightness(float value) {
790         return MathUtils.constrain(value,
791                 mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
792     }
793 
prepareBrightnessAdjustmentSample()794     private void prepareBrightnessAdjustmentSample() {
795         if (!mBrightnessAdjustmentSamplePending) {
796             mBrightnessAdjustmentSamplePending = true;
797             mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1;
798             mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness;
799         } else {
800             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
801         }
802 
803         mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE,
804                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
805     }
806 
cancelBrightnessAdjustmentSample()807     private void cancelBrightnessAdjustmentSample() {
808         if (mBrightnessAdjustmentSamplePending) {
809             mBrightnessAdjustmentSamplePending = false;
810             mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
811         }
812     }
813 
collectBrightnessAdjustmentSample()814     private void collectBrightnessAdjustmentSample() {
815         if (mBrightnessAdjustmentSamplePending) {
816             mBrightnessAdjustmentSamplePending = false;
817             if (mAmbientLuxValid && (mScreenAutoBrightness >= PowerManager.BRIGHTNESS_MIN
818                     || mScreenAutoBrightness == PowerManager.BRIGHTNESS_OFF_FLOAT)) {
819                 if (mLoggingEnabled) {
820                     Slog.d(TAG, "Auto-brightness adjustment changed by user: "
821                             + "lux=" + mAmbientLux + ", "
822                             + "brightness=" + mScreenAutoBrightness + ", "
823                             + "ring=" + mAmbientLightRingBuffer);
824                 }
825 
826                 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
827                         mBrightnessAdjustmentSampleOldLux,
828                         mBrightnessAdjustmentSampleOldBrightness,
829                         mAmbientLux,
830                         mScreenAutoBrightness);
831             }
832         }
833     }
834 
835     // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
836     // foreground app's package name and category and correct the brightness accordingly.
registerForegroundAppUpdater()837     private void registerForegroundAppUpdater() {
838         try {
839             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
840             // This will not get called until the foreground app changes for the first time, so
841             // call it explicitly to get the current foreground app's info.
842             updateForegroundApp();
843         } catch (RemoteException e) {
844             if (mLoggingEnabled) {
845                 Slog.e(TAG, "Failed to register foreground app updater: " + e);
846             }
847             // Nothing to do.
848         }
849     }
850 
unregisterForegroundAppUpdater()851     private void unregisterForegroundAppUpdater() {
852         try {
853             mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
854         } catch (RemoteException e) {
855             // Nothing to do.
856         }
857         mForegroundAppPackageName = null;
858         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
859     }
860 
861     // Set the foreground app's package name and category, so brightness can be corrected per app.
updateForegroundApp()862     private void updateForegroundApp() {
863         if (mLoggingEnabled) {
864             Slog.d(TAG, "Attempting to update foreground app");
865         }
866         // The ActivityTaskManager's lock tends to get contended, so this is done in a background
867         // thread and applied via this thread's handler synchronously.
868         mInjector.getBackgroundThreadHandler().post(new Runnable() {
869             public void run() {
870                 try {
871                     // The foreground app is the top activity of the focused tasks stack.
872                     final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo();
873                     if (info == null || info.topActivity == null) {
874                         return;
875                     }
876                     final String packageName = info.topActivity.getPackageName();
877                     // If the app didn't change, there's nothing to do. Otherwise, we have to
878                     // update the category and re-apply the brightness correction.
879                     if (mForegroundAppPackageName != null
880                             && mForegroundAppPackageName.equals(packageName)) {
881                         return;
882                     }
883                     mPendingForegroundAppPackageName = packageName;
884                     mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
885                     try {
886                         ApplicationInfo app = mPackageManager.getApplicationInfo(packageName,
887                                 PackageManager.MATCH_ANY_USER);
888                         mPendingForegroundAppCategory = app.category;
889                     } catch (PackageManager.NameNotFoundException e) {
890                         // Nothing to do
891                     }
892                     mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
893                 } catch (RemoteException e) {
894                     // Nothing to do
895                 }
896             }
897         });
898     }
899 
updateForegroundAppSync()900     private void updateForegroundAppSync() {
901         if (mLoggingEnabled) {
902             Slog.d(TAG, "Updating foreground app: packageName=" + mPendingForegroundAppPackageName
903                     + ", category=" + mPendingForegroundAppCategory);
904         }
905         mForegroundAppPackageName = mPendingForegroundAppPackageName;
906         mPendingForegroundAppPackageName = null;
907         mForegroundAppCategory = mPendingForegroundAppCategory;
908         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
909         updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
910     }
911 
912     private final class AutomaticBrightnessHandler extends Handler {
AutomaticBrightnessHandler(Looper looper)913         public AutomaticBrightnessHandler(Looper looper) {
914             super(looper, null, true /*async*/);
915         }
916 
917         @Override
handleMessage(Message msg)918         public void handleMessage(Message msg) {
919             switch (msg.what) {
920                 case MSG_RUN_UPDATE:
921                     updateAutoBrightness(true /*sendUpdate*/, false /*isManuallySet*/);
922                     break;
923 
924                 case MSG_UPDATE_AMBIENT_LUX:
925                     updateAmbientLux();
926                     break;
927 
928                 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
929                     collectBrightnessAdjustmentSample();
930                     break;
931 
932                 case MSG_INVALIDATE_SHORT_TERM_MODEL:
933                     invalidateShortTermModel();
934                     break;
935 
936                 case MSG_UPDATE_FOREGROUND_APP:
937                     updateForegroundApp();
938                     break;
939 
940                 case MSG_UPDATE_FOREGROUND_APP_SYNC:
941                     updateForegroundAppSync();
942                     break;
943             }
944         }
945     }
946 
947     private final SensorEventListener mLightSensorListener = new SensorEventListener() {
948         @Override
949         public void onSensorChanged(SensorEvent event) {
950             if (mLightSensorEnabled) {
951                 final long time = SystemClock.uptimeMillis();
952                 final float lux = event.values[0];
953                 handleLightSensorEvent(time, lux);
954             }
955         }
956 
957         @Override
958         public void onAccuracyChanged(Sensor sensor, int accuracy) {
959             // Not used.
960         }
961     };
962 
963     // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
964     // moving to top.
965     class TaskStackListenerImpl extends TaskStackListener {
966         @Override
onTaskStackChanged()967         public void onTaskStackChanged() {
968             mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
969         }
970     }
971 
972     /** Callbacks to request updates to the display's power state. */
973     interface Callbacks {
updateBrightness()974         void updateBrightness();
975     }
976 
977     /**
978      * A ring buffer of ambient light measurements sorted by time.
979      *
980      * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
981      * from oldest to newest.
982      */
983     private static final class AmbientLightRingBuffer {
984         // Proportional extra capacity of the buffer beyond the expected number of light samples
985         // in the horizon
986         private static final float BUFFER_SLACK = 1.5f;
987         private float[] mRingLux;
988         private long[] mRingTime;
989         private int mCapacity;
990 
991         // The first valid element and the next open slot.
992         // Note that if mCount is zero then there are no valid elements.
993         private int mStart;
994         private int mEnd;
995         private int mCount;
996 
AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon)997         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
998             if (lightSensorRate <= 0) {
999                 throw new IllegalArgumentException("lightSensorRate must be above 0");
1000             }
1001             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
1002             mRingLux = new float[mCapacity];
1003             mRingTime = new long[mCapacity];
1004         }
1005 
getLux(int index)1006         public float getLux(int index) {
1007             return mRingLux[offsetOf(index)];
1008         }
1009 
getTime(int index)1010         public long getTime(int index) {
1011             return mRingTime[offsetOf(index)];
1012         }
1013 
push(long time, float lux)1014         public void push(long time, float lux) {
1015             int next = mEnd;
1016             if (mCount == mCapacity) {
1017                 int newSize = mCapacity * 2;
1018 
1019                 float[] newRingLux = new float[newSize];
1020                 long[] newRingTime = new long[newSize];
1021                 int length = mCapacity - mStart;
1022                 System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
1023                 System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
1024                 if (mStart != 0) {
1025                     System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
1026                     System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
1027                 }
1028                 mRingLux = newRingLux;
1029                 mRingTime = newRingTime;
1030 
1031                 next = mCapacity;
1032                 mCapacity = newSize;
1033                 mStart = 0;
1034             }
1035             mRingTime[next] = time;
1036             mRingLux[next] = lux;
1037             mEnd = next + 1;
1038             if (mEnd == mCapacity) {
1039                 mEnd = 0;
1040             }
1041             mCount++;
1042         }
1043 
prune(long horizon)1044         public void prune(long horizon) {
1045             if (mCount == 0) {
1046                 return;
1047             }
1048 
1049             while (mCount > 1) {
1050                 int next = mStart + 1;
1051                 if (next >= mCapacity) {
1052                     next -= mCapacity;
1053                 }
1054                 if (mRingTime[next] > horizon) {
1055                     // Some light sensors only produce data upon a change in the ambient light
1056                     // levels, so we need to consider the previous measurement as the ambient light
1057                     // level for all points in time up until we receive a new measurement. Thus, we
1058                     // always want to keep the youngest element that would be removed from the
1059                     // buffer and just set its measurement time to the horizon time since at that
1060                     // point it is the ambient light level, and to remove it would be to drop a
1061                     // valid data point within our horizon.
1062                     break;
1063                 }
1064                 mStart = next;
1065                 mCount -= 1;
1066             }
1067 
1068             if (mRingTime[mStart] < horizon) {
1069                 mRingTime[mStart] = horizon;
1070             }
1071         }
1072 
size()1073         public int size() {
1074             return mCount;
1075         }
1076 
clear()1077         public void clear() {
1078             mStart = 0;
1079             mEnd = 0;
1080             mCount = 0;
1081         }
1082 
1083         @Override
toString()1084         public String toString() {
1085             StringBuilder buf = new StringBuilder();
1086             buf.append('[');
1087             for (int i = 0; i < mCount; i++) {
1088                 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
1089                 if (i != 0) {
1090                     buf.append(", ");
1091                 }
1092                 buf.append(getLux(i));
1093                 buf.append(" / ");
1094                 buf.append(next - getTime(i));
1095                 buf.append("ms");
1096             }
1097             buf.append(']');
1098             return buf.toString();
1099         }
1100 
1101         private int offsetOf(int index) {
1102             if (index >= mCount || index < 0) {
1103                 throw new ArrayIndexOutOfBoundsException(index);
1104             }
1105             index += mStart;
1106             if (index >= mCapacity) {
1107                 index -= mCapacity;
1108             }
1109             return index;
1110         }
1111     }
1112 
1113     public static class Injector {
getBackgroundThreadHandler()1114         public Handler getBackgroundThreadHandler() {
1115             return BackgroundThread.getHandler();
1116         }
1117     }
1118 }
1119