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