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