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