AutomaticBrightnessController.java revision c135225466eb1b209e12c8c2df135641a21c5d66
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 17package com.android.server.display; 18 19import com.android.server.EventLogTags; 20import com.android.server.LocalServices; 21 22import android.annotation.Nullable; 23import android.hardware.Sensor; 24import android.hardware.SensorEvent; 25import android.hardware.SensorEventListener; 26import android.hardware.SensorManager; 27import android.hardware.display.BrightnessConfiguration; 28import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; 29import android.os.Handler; 30import android.os.Looper; 31import android.os.Message; 32import android.os.PowerManager; 33import android.os.SystemClock; 34import android.os.Trace; 35import android.text.format.DateUtils; 36import android.util.EventLog; 37import android.util.MathUtils; 38import android.util.Slog; 39import android.util.TimeUtils; 40 41import java.io.PrintWriter; 42 43class AutomaticBrightnessController { 44 private static final String TAG = "AutomaticBrightnessController"; 45 46 private static final boolean DEBUG = false; 47 private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; 48 49 // If true, enables the use of the screen auto-brightness adjustment setting. 50 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true; 51 52 // How long the current sensor reading is assumed to be valid beyond the current time. 53 // This provides a bit of prediction, as well as ensures that the weight for the last sample is 54 // non-zero, which in turn ensures that the total weight is non-zero. 55 private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100; 56 57 // Debounce for sampling user-initiated changes in display brightness to ensure 58 // the user is satisfied with the result before storing the sample. 59 private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000; 60 61 // Timeout after which we remove the effects any user interactions might've had on the 62 // brightness mapping. This timeout doesn't start until we transition to a non-interactive 63 // display policy so that we don't reset while users are using their devices, but also so that 64 // we don't erroneously keep the short-term model if the device is dozing but the display is 65 // fully on. 66 private static final int SHORT_TERM_MODEL_TIMEOUT_MILLIS = 30000; 67 68 private static final int MSG_UPDATE_AMBIENT_LUX = 1; 69 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2; 70 private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3; 71 72 // Length of the ambient light horizon used to calculate the long term estimate of ambient 73 // light. 74 private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000; 75 76 // Length of the ambient light horizon used to calculate short-term estimate of ambient light. 77 private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000; 78 79 // Callbacks for requesting updates to the display's power state 80 private final Callbacks mCallbacks; 81 82 // The sensor manager. 83 private final SensorManager mSensorManager; 84 85 // The light sensor, or null if not available or needed. 86 private final Sensor mLightSensor; 87 88 // The mapper to translate ambient lux to screen brightness in the range [0, 1.0]. 89 private final BrightnessMappingStrategy mBrightnessMapper; 90 91 // The minimum and maximum screen brightnesses. 92 private final int mScreenBrightnessRangeMinimum; 93 private final int mScreenBrightnessRangeMaximum; 94 private final float mDozeScaleFactor; 95 96 // Initial light sensor event rate in milliseconds. 97 private final int mInitialLightSensorRate; 98 99 // Steady-state light sensor event rate in milliseconds. 100 private final int mNormalLightSensorRate; 101 102 // The current light sensor event rate in milliseconds. 103 private int mCurrentLightSensorRate; 104 105 // Stability requirements in milliseconds for accepting a new brightness level. This is used 106 // for debouncing the light sensor. Different constants are used to debounce the light sensor 107 // when adapting to brighter or darker environments. This parameter controls how quickly 108 // brightness changes occur in response to an observed change in light level that exceeds the 109 // hysteresis threshold. 110 private final long mBrighteningLightDebounceConfig; 111 private final long mDarkeningLightDebounceConfig; 112 113 // If true immediately after the screen is turned on the controller will try to adjust the 114 // brightness based on the current sensor reads. If false, the controller will collect more data 115 // and only then decide whether to change brightness. 116 private final boolean mResetAmbientLuxAfterWarmUpConfig; 117 118 // Period of time in which to consider light samples in milliseconds. 119 private final int mAmbientLightHorizon; 120 121 // The intercept used for the weighting calculation. This is used in order to keep all possible 122 // weighting values positive. 123 private final int mWeightingIntercept; 124 125 // accessor object for determining thresholds to change brightness dynamically 126 private final HysteresisLevels mDynamicHysteresis; 127 128 // Amount of time to delay auto-brightness after screen on while waiting for 129 // the light sensor to warm-up in milliseconds. 130 // May be 0 if no warm-up is required. 131 private int mLightSensorWarmUpTimeConfig; 132 133 // Set to true if the light sensor is enabled. 134 private boolean mLightSensorEnabled; 135 136 // The time when the light sensor was enabled. 137 private long mLightSensorEnableTime; 138 139 // The currently accepted nominal ambient light level. 140 private float mAmbientLux; 141 142 // True if mAmbientLux holds a valid value. 143 private boolean mAmbientLuxValid; 144 145 // The ambient light level threshold at which to brighten or darken the screen. 146 private float mBrighteningLuxThreshold; 147 private float mDarkeningLuxThreshold; 148 149 // The most recent light sample. 150 private float mLastObservedLux; 151 152 // The time of the most light recent sample. 153 private long mLastObservedLuxTime; 154 155 // The number of light samples collected since the light sensor was enabled. 156 private int mRecentLightSamples; 157 158 // A ring buffer containing all of the recent ambient light sensor readings. 159 private AmbientLightRingBuffer mAmbientLightRingBuffer; 160 161 // The handler 162 private AutomaticBrightnessHandler mHandler; 163 164 // The screen brightness level that has been chosen by the auto-brightness 165 // algorithm. The actual brightness should ramp towards this value. 166 // We preserve this value even when we stop using the light sensor so 167 // that we can quickly revert to the previous auto-brightness level 168 // while the light sensor warms up. 169 // Use -1 if there is no current auto-brightness value available. 170 private int mScreenAutoBrightness = -1; 171 172 // The current display policy. This is useful, for example, for knowing when we're dozing, 173 // where the light sensor may not be available. 174 private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF; 175 176 // True if we are collecting a brightness adjustment sample, along with some data 177 // for the initial state of the sample. 178 private boolean mBrightnessAdjustmentSamplePending; 179 private float mBrightnessAdjustmentSampleOldLux; 180 private int mBrightnessAdjustmentSampleOldBrightness; 181 182 // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the 183 // user's adjustment) immediately, but wait for a drastic enough change in the ambient light. 184 // The anchor determines what were the light levels when the user has set her preference, and 185 // we use a relative threshold to determine when to revert to the OEM curve. 186 private boolean mShortTermModelValid; 187 private float mShortTermModelAnchor; 188 private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; 189 190 public AutomaticBrightnessController(Callbacks callbacks, Looper looper, 191 SensorManager sensorManager, BrightnessMappingStrategy mapper, 192 int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, 193 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, 194 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, 195 int ambientLightHorizon, HysteresisLevels dynamicHysteresis) { 196 mCallbacks = callbacks; 197 mSensorManager = sensorManager; 198 mBrightnessMapper = mapper; 199 mScreenBrightnessRangeMinimum = brightnessMin; 200 mScreenBrightnessRangeMaximum = brightnessMax; 201 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; 202 mDozeScaleFactor = dozeScaleFactor; 203 mNormalLightSensorRate = lightSensorRate; 204 mInitialLightSensorRate = initialLightSensorRate; 205 mCurrentLightSensorRate = -1; 206 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig; 207 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig; 208 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig; 209 mAmbientLightHorizon = ambientLightHorizon; 210 mWeightingIntercept = ambientLightHorizon; 211 mDynamicHysteresis = dynamicHysteresis; 212 mShortTermModelValid = true; 213 mShortTermModelAnchor = -1; 214 215 mHandler = new AutomaticBrightnessHandler(looper); 216 mAmbientLightRingBuffer = 217 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon); 218 219 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) { 220 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); 221 } 222 } 223 224 public int getAutomaticScreenBrightness() { 225 if (!mAmbientLuxValid) { 226 return -1; 227 } 228 if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) { 229 return (int) (mScreenAutoBrightness * mDozeScaleFactor); 230 } 231 return mScreenAutoBrightness; 232 } 233 234 public float getAutomaticScreenBrightnessAdjustment() { 235 return mBrightnessMapper.getAutoBrightnessAdjustment(); 236 } 237 238 public void configure(boolean enable, @Nullable BrightnessConfiguration configuration, 239 float brightness, boolean userChangedBrightness, float adjustment, 240 boolean userChangedAutoBrightnessAdjustment, int displayPolicy) { 241 // While dozing, the application processor may be suspended which will prevent us from 242 // receiving new information from the light sensor. On some devices, we may be able to 243 // switch to a wake-up light sensor instead but for now we will simply disable the sensor 244 // and hold onto the last computed screen auto brightness. We save the dozing flag for 245 // debugging purposes. 246 boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE); 247 boolean changed = setBrightnessConfiguration(configuration); 248 changed |= setDisplayPolicy(displayPolicy); 249 if (userChangedAutoBrightnessAdjustment) { 250 changed |= setAutoBrightnessAdjustment(adjustment); 251 } 252 if (userChangedBrightness && enable) { 253 // Update the brightness curve with the new user control point. It's critical this 254 // happens after we update the autobrightness adjustment since it may reset it. 255 changed |= setScreenBrightnessByUser(brightness); 256 } 257 final boolean userInitiatedChange = 258 userChangedBrightness || userChangedAutoBrightnessAdjustment; 259 if (userInitiatedChange && enable && !dozing) { 260 prepareBrightnessAdjustmentSample(); 261 } 262 changed |= setLightSensorEnabled(enable && !dozing); 263 if (changed) { 264 updateAutoBrightness(false /*sendUpdate*/); 265 } 266 } 267 268 public boolean hasUserDataPoints() { 269 return mBrightnessMapper.hasUserDataPoints(); 270 } 271 272 public boolean isDefaultConfig() { 273 return mBrightnessMapper.isDefaultConfig(); 274 } 275 276 public BrightnessConfiguration getDefaultConfig() { 277 return mBrightnessMapper.getDefaultConfig(); 278 } 279 280 private boolean setDisplayPolicy(int policy) { 281 if (mDisplayPolicy == policy) { 282 return false; 283 } 284 final int oldPolicy = mDisplayPolicy; 285 mDisplayPolicy = policy; 286 if (DEBUG) { 287 Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy); 288 } 289 if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) { 290 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL, 291 SHORT_TERM_MODEL_TIMEOUT_MILLIS); 292 } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) { 293 mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL); 294 } 295 return true; 296 } 297 298 private static boolean isInteractivePolicy(int policy) { 299 return policy == DisplayPowerRequest.POLICY_BRIGHT 300 || policy == DisplayPowerRequest.POLICY_DIM 301 || policy == DisplayPowerRequest.POLICY_VR; 302 } 303 304 private boolean setScreenBrightnessByUser(float brightness) { 305 if (!mAmbientLuxValid) { 306 // If we don't have a valid ambient lux then we don't have a valid brightness anyways, 307 // and we can't use this data to add a new control point to the short-term model. 308 return false; 309 } 310 mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness); 311 mShortTermModelValid = true; 312 mShortTermModelAnchor = mAmbientLux; 313 if (DEBUG) { 314 Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor); 315 } 316 return true; 317 } 318 319 public void resetShortTermModel() { 320 mBrightnessMapper.clearUserDataPoints(); 321 mShortTermModelValid = true; 322 mShortTermModelAnchor = -1; 323 } 324 325 private void invalidateShortTermModel() { 326 if (DEBUG) { 327 Slog.d(TAG, "ShortTermModel: invalidate user data"); 328 } 329 mShortTermModelValid = false; 330 } 331 332 public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) { 333 return mBrightnessMapper.setBrightnessConfiguration(configuration); 334 } 335 336 public void dump(PrintWriter pw) { 337 pw.println(); 338 pw.println("Automatic Brightness Controller Configuration:"); 339 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); 340 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); 341 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); 342 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig); 343 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig); 344 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig); 345 346 pw.println(); 347 pw.println("Automatic Brightness Controller State:"); 348 pw.println(" mLightSensor=" + mLightSensor); 349 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); 350 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime)); 351 pw.println(" mAmbientLux=" + mAmbientLux); 352 pw.println(" mAmbientLuxValid=" + mAmbientLuxValid); 353 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon); 354 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold); 355 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold); 356 pw.println(" mLastObservedLux=" + mLastObservedLux); 357 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime)); 358 pw.println(" mRecentLightSamples=" + mRecentLightSamples); 359 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer); 360 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); 361 pw.println(" mDisplayPolicy=" + mDisplayPolicy); 362 pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor); 363 364 pw.println(); 365 mBrightnessMapper.dump(pw); 366 } 367 368 private boolean setLightSensorEnabled(boolean enable) { 369 if (enable) { 370 if (!mLightSensorEnabled) { 371 mLightSensorEnabled = true; 372 mLightSensorEnableTime = SystemClock.uptimeMillis(); 373 mCurrentLightSensorRate = mInitialLightSensorRate; 374 mSensorManager.registerListener(mLightSensorListener, mLightSensor, 375 mCurrentLightSensorRate * 1000, mHandler); 376 return true; 377 } 378 } else if (mLightSensorEnabled) { 379 mLightSensorEnabled = false; 380 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; 381 mRecentLightSamples = 0; 382 mAmbientLightRingBuffer.clear(); 383 mCurrentLightSensorRate = -1; 384 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); 385 mSensorManager.unregisterListener(mLightSensorListener); 386 } 387 return false; 388 } 389 390 private void handleLightSensorEvent(long time, float lux) { 391 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux); 392 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); 393 394 if (mAmbientLightRingBuffer.size() == 0) { 395 // switch to using the steady-state sample rate after grabbing the initial light sample 396 adjustLightSensorRate(mNormalLightSensorRate); 397 } 398 applyLightSensorMeasurement(time, lux); 399 updateAmbientLux(time); 400 } 401 402 private void applyLightSensorMeasurement(long time, float lux) { 403 mRecentLightSamples++; 404 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon); 405 mAmbientLightRingBuffer.push(time, lux); 406 407 // Remember this sample value. 408 mLastObservedLux = lux; 409 mLastObservedLuxTime = time; 410 } 411 412 private void adjustLightSensorRate(int lightSensorRate) { 413 // if the light sensor rate changed, update the sensor listener 414 if (lightSensorRate != mCurrentLightSensorRate) { 415 if (DEBUG) { 416 Slog.d(TAG, "adjustLightSensorRate: " + 417 "previousRate=" + mCurrentLightSensorRate + ", " + 418 "currentRate=" + lightSensorRate); 419 } 420 mCurrentLightSensorRate = lightSensorRate; 421 mSensorManager.unregisterListener(mLightSensorListener); 422 mSensorManager.registerListener(mLightSensorListener, mLightSensor, 423 lightSensorRate * 1000, mHandler); 424 } 425 } 426 427 private boolean setAutoBrightnessAdjustment(float adjustment) { 428 return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment); 429 } 430 431 private void setAmbientLux(float lux) { 432 if (DEBUG) { 433 Slog.d(TAG, "setAmbientLux(" + lux + ")"); 434 } 435 if (lux < 0) { 436 Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0"); 437 lux = 0; 438 } 439 mAmbientLux = lux; 440 mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux); 441 mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux); 442 443 // If the short term model was invalidated and the change is drastic enough, reset it. 444 if (!mShortTermModelValid && mShortTermModelAnchor != -1) { 445 final float minAmbientLux = 446 mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO; 447 final float maxAmbientLux = 448 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO; 449 if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) { 450 if (DEBUG) { 451 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " + 452 minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux); 453 } 454 mShortTermModelValid = true; 455 } else { 456 Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + mAmbientLux + 457 "(" + minAmbientLux + ", " + maxAmbientLux + ")"); 458 resetShortTermModel(); 459 } 460 } 461 } 462 463 private float calculateAmbientLux(long now, long horizon) { 464 if (DEBUG) { 465 Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")"); 466 } 467 final int N = mAmbientLightRingBuffer.size(); 468 if (N == 0) { 469 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available"); 470 return -1; 471 } 472 473 // Find the first measurement that is just outside of the horizon. 474 int endIndex = 0; 475 final long horizonStartTime = now - horizon; 476 for (int i = 0; i < N-1; i++) { 477 if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) { 478 endIndex++; 479 } else { 480 break; 481 } 482 } 483 if (DEBUG) { 484 Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" + 485 mAmbientLightRingBuffer.getTime(endIndex) + ", " + 486 mAmbientLightRingBuffer.getLux(endIndex) + ")"); 487 } 488 float sum = 0; 489 float totalWeight = 0; 490 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS; 491 for (int i = N - 1; i >= endIndex; i--) { 492 long eventTime = mAmbientLightRingBuffer.getTime(i); 493 if (i == endIndex && eventTime < horizonStartTime) { 494 // If we're at the final value, make sure we only consider the part of the sample 495 // within our desired horizon. 496 eventTime = horizonStartTime; 497 } 498 final long startTime = eventTime - now; 499 float weight = calculateWeight(startTime, endTime); 500 float lux = mAmbientLightRingBuffer.getLux(i); 501 if (DEBUG) { 502 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " + 503 "lux=" + lux + ", " + 504 "weight=" + weight); 505 } 506 totalWeight += weight; 507 sum += lux * weight; 508 endTime = startTime; 509 } 510 if (DEBUG) { 511 Slog.d(TAG, "calculateAmbientLux: " + 512 "totalWeight=" + totalWeight + ", " + 513 "newAmbientLux=" + (sum / totalWeight)); 514 } 515 return sum / totalWeight; 516 } 517 518 private float calculateWeight(long startDelta, long endDelta) { 519 return weightIntegral(endDelta) - weightIntegral(startDelta); 520 } 521 522 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the 523 // horizon we're looking at and provides a non-linear weighting for light samples. 524 private float weightIntegral(long x) { 525 return x * (x * 0.5f + mWeightingIntercept); 526 } 527 528 private long nextAmbientLightBrighteningTransition(long time) { 529 final int N = mAmbientLightRingBuffer.size(); 530 long earliestValidTime = time; 531 for (int i = N - 1; i >= 0; i--) { 532 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) { 533 break; 534 } 535 earliestValidTime = mAmbientLightRingBuffer.getTime(i); 536 } 537 return earliestValidTime + mBrighteningLightDebounceConfig; 538 } 539 540 private long nextAmbientLightDarkeningTransition(long time) { 541 final int N = mAmbientLightRingBuffer.size(); 542 long earliestValidTime = time; 543 for (int i = N - 1; i >= 0; i--) { 544 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) { 545 break; 546 } 547 earliestValidTime = mAmbientLightRingBuffer.getTime(i); 548 } 549 return earliestValidTime + mDarkeningLightDebounceConfig; 550 } 551 552 private void updateAmbientLux() { 553 long time = SystemClock.uptimeMillis(); 554 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon); 555 updateAmbientLux(time); 556 } 557 558 private void updateAmbientLux(long time) { 559 // If the light sensor was just turned on then immediately update our initial 560 // estimate of the current ambient light level. 561 if (!mAmbientLuxValid) { 562 final long timeWhenSensorWarmedUp = 563 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime; 564 if (time < timeWhenSensorWarmedUp) { 565 if (DEBUG) { 566 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " + 567 "time=" + time + ", " + 568 "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp); 569 } 570 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, 571 timeWhenSensorWarmedUp); 572 return; 573 } 574 setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS)); 575 mAmbientLuxValid = true; 576 if (DEBUG) { 577 Slog.d(TAG, "updateAmbientLux: Initializing: " + 578 "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " + 579 "mAmbientLux=" + mAmbientLux); 580 } 581 updateAutoBrightness(true); 582 } 583 584 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time); 585 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time); 586 // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term 587 // change in lighting conditions, and a fast ambient lux to determine what the new 588 // brightness situation is since the slow lux can be quite slow to converge. 589 // 590 // Note that both values need to be checked for sufficient change before updating the 591 // proposed ambient light value since the slow value might be sufficiently far enough away 592 // from the fast value to cause a recalculation while its actually just converging on 593 // the fast value still. 594 float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS); 595 float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS); 596 597 if ((slowAmbientLux >= mBrighteningLuxThreshold && 598 fastAmbientLux >= mBrighteningLuxThreshold && 599 nextBrightenTransition <= time) 600 || 601 (slowAmbientLux <= mDarkeningLuxThreshold && 602 fastAmbientLux <= mDarkeningLuxThreshold && 603 nextDarkenTransition <= time)) { 604 setAmbientLux(fastAmbientLux); 605 if (DEBUG) { 606 Slog.d(TAG, "updateAmbientLux: " + 607 ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " + 608 "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold + ", " + 609 "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " + 610 "mAmbientLux=" + mAmbientLux); 611 } 612 updateAutoBrightness(true); 613 nextBrightenTransition = nextAmbientLightBrighteningTransition(time); 614 nextDarkenTransition = nextAmbientLightDarkeningTransition(time); 615 } 616 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition); 617 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't 618 // exceed the necessary threshold, then it's possible we'll get a transition time prior to 619 // now. Rather than continually checking to see whether the weighted lux exceeds the 620 // threshold, schedule an update for when we'd normally expect another light sample, which 621 // should be enough time to decide whether we should actually transition to the new 622 // weighted ambient lux or not. 623 nextTransitionTime = 624 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate; 625 if (DEBUG) { 626 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " + 627 nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime)); 628 } 629 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime); 630 } 631 632 private void updateAutoBrightness(boolean sendUpdate) { 633 if (!mAmbientLuxValid) { 634 return; 635 } 636 637 float value = mBrightnessMapper.getBrightness(mAmbientLux); 638 639 int newScreenAutoBrightness = 640 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); 641 if (mScreenAutoBrightness != newScreenAutoBrightness) { 642 if (DEBUG) { 643 Slog.d(TAG, "updateAutoBrightness: " + 644 "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " + 645 "newScreenAutoBrightness=" + newScreenAutoBrightness); 646 } 647 648 mScreenAutoBrightness = newScreenAutoBrightness; 649 if (sendUpdate) { 650 mCallbacks.updateBrightness(); 651 } 652 } 653 } 654 655 private int clampScreenBrightness(int value) { 656 return MathUtils.constrain(value, 657 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); 658 } 659 660 private void prepareBrightnessAdjustmentSample() { 661 if (!mBrightnessAdjustmentSamplePending) { 662 mBrightnessAdjustmentSamplePending = true; 663 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1; 664 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness; 665 } else { 666 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 667 } 668 669 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE, 670 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS); 671 } 672 673 private void cancelBrightnessAdjustmentSample() { 674 if (mBrightnessAdjustmentSamplePending) { 675 mBrightnessAdjustmentSamplePending = false; 676 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 677 } 678 } 679 680 private void collectBrightnessAdjustmentSample() { 681 if (mBrightnessAdjustmentSamplePending) { 682 mBrightnessAdjustmentSamplePending = false; 683 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) { 684 if (DEBUG) { 685 Slog.d(TAG, "Auto-brightness adjustment changed by user: " + 686 "lux=" + mAmbientLux + ", " + 687 "brightness=" + mScreenAutoBrightness + ", " + 688 "ring=" + mAmbientLightRingBuffer); 689 } 690 691 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ, 692 mBrightnessAdjustmentSampleOldLux, 693 mBrightnessAdjustmentSampleOldBrightness, 694 mAmbientLux, 695 mScreenAutoBrightness); 696 } 697 } 698 } 699 700 private final class AutomaticBrightnessHandler extends Handler { 701 public AutomaticBrightnessHandler(Looper looper) { 702 super(looper, null, true /*async*/); 703 } 704 705 @Override 706 public void handleMessage(Message msg) { 707 switch (msg.what) { 708 case MSG_UPDATE_AMBIENT_LUX: 709 updateAmbientLux(); 710 break; 711 712 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE: 713 collectBrightnessAdjustmentSample(); 714 break; 715 716 case MSG_INVALIDATE_SHORT_TERM_MODEL: 717 invalidateShortTermModel(); 718 break; 719 } 720 } 721 } 722 723 private final SensorEventListener mLightSensorListener = new SensorEventListener() { 724 @Override 725 public void onSensorChanged(SensorEvent event) { 726 if (mLightSensorEnabled) { 727 final long time = SystemClock.uptimeMillis(); 728 final float lux = event.values[0]; 729 handleLightSensorEvent(time, lux); 730 } 731 } 732 733 @Override 734 public void onAccuracyChanged(Sensor sensor, int accuracy) { 735 // Not used. 736 } 737 }; 738 739 /** Callbacks to request updates to the display's power state. */ 740 interface Callbacks { 741 void updateBrightness(); 742 } 743 744 /** 745 * A ring buffer of ambient light measurements sorted by time. 746 * 747 * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted 748 * from oldest to newest. 749 */ 750 private static final class AmbientLightRingBuffer { 751 // Proportional extra capacity of the buffer beyond the expected number of light samples 752 // in the horizon 753 private static final float BUFFER_SLACK = 1.5f; 754 private float[] mRingLux; 755 private long[] mRingTime; 756 private int mCapacity; 757 758 // The first valid element and the next open slot. 759 // Note that if mCount is zero then there are no valid elements. 760 private int mStart; 761 private int mEnd; 762 private int mCount; 763 764 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) { 765 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate); 766 mRingLux = new float[mCapacity]; 767 mRingTime = new long[mCapacity]; 768 } 769 770 public float getLux(int index) { 771 return mRingLux[offsetOf(index)]; 772 } 773 774 public long getTime(int index) { 775 return mRingTime[offsetOf(index)]; 776 } 777 778 public void push(long time, float lux) { 779 int next = mEnd; 780 if (mCount == mCapacity) { 781 int newSize = mCapacity * 2; 782 783 float[] newRingLux = new float[newSize]; 784 long[] newRingTime = new long[newSize]; 785 int length = mCapacity - mStart; 786 System.arraycopy(mRingLux, mStart, newRingLux, 0, length); 787 System.arraycopy(mRingTime, mStart, newRingTime, 0, length); 788 if (mStart != 0) { 789 System.arraycopy(mRingLux, 0, newRingLux, length, mStart); 790 System.arraycopy(mRingTime, 0, newRingTime, length, mStart); 791 } 792 mRingLux = newRingLux; 793 mRingTime = newRingTime; 794 795 next = mCapacity; 796 mCapacity = newSize; 797 mStart = 0; 798 } 799 mRingTime[next] = time; 800 mRingLux[next] = lux; 801 mEnd = next + 1; 802 if (mEnd == mCapacity) { 803 mEnd = 0; 804 } 805 mCount++; 806 } 807 808 public void prune(long horizon) { 809 if (mCount == 0) { 810 return; 811 } 812 813 while (mCount > 1) { 814 int next = mStart + 1; 815 if (next >= mCapacity) { 816 next -= mCapacity; 817 } 818 if (mRingTime[next] > horizon) { 819 // Some light sensors only produce data upon a change in the ambient light 820 // levels, so we need to consider the previous measurement as the ambient light 821 // level for all points in time up until we receive a new measurement. Thus, we 822 // always want to keep the youngest element that would be removed from the 823 // buffer and just set its measurement time to the horizon time since at that 824 // point it is the ambient light level, and to remove it would be to drop a 825 // valid data point within our horizon. 826 break; 827 } 828 mStart = next; 829 mCount -= 1; 830 } 831 832 if (mRingTime[mStart] < horizon) { 833 mRingTime[mStart] = horizon; 834 } 835 } 836 837 public int size() { 838 return mCount; 839 } 840 841 public void clear() { 842 mStart = 0; 843 mEnd = 0; 844 mCount = 0; 845 } 846 847 @Override 848 public String toString() { 849 StringBuffer buf = new StringBuffer(); 850 buf.append('['); 851 for (int i = 0; i < mCount; i++) { 852 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis(); 853 if (i != 0) { 854 buf.append(", "); 855 } 856 buf.append(getLux(i)); 857 buf.append(" / "); 858 buf.append(next - getTime(i)); 859 buf.append("ms"); 860 } 861 buf.append(']'); 862 return buf.toString(); 863 } 864 865 private int offsetOf(int index) { 866 if (index >= mCount || index < 0) { 867 throw new ArrayIndexOutOfBoundsException(index); 868 } 869 index += mStart; 870 if (index >= mCapacity) { 871 index -= mCapacity; 872 } 873 return index; 874 } 875 } 876} 877