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