AutomaticBrightnessController.java revision 53d0661f38d6ada39aefaac5ce016f802e74bd44
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_RESET_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 // A ring buffer containing the light sensor readings for the initial horizon period. 162 private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer; 163 164 // The handler 165 private AutomaticBrightnessHandler mHandler; 166 167 // The screen brightness level that has been chosen by the auto-brightness 168 // algorithm. The actual brightness should ramp towards this value. 169 // We preserve this value even when we stop using the light sensor so 170 // that we can quickly revert to the previous auto-brightness level 171 // while the light sensor warms up. 172 // Use -1 if there is no current auto-brightness value available. 173 private int mScreenAutoBrightness = -1; 174 175 // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter) 176 private float mScreenAutoBrightnessAdjustment = 0.0f; 177 178 // The maximum range of gamma adjustment possible using the screen 179 // auto-brightness adjustment setting. 180 private float mScreenAutoBrightnessAdjustmentMaxGamma; 181 182 // The last screen auto-brightness gamma. (For printing in dump() only.) 183 private float mLastScreenAutoBrightnessGamma = 1.0f; 184 185 // The current display policy. This is useful, for example, for knowing when we're dozing, 186 // where the light sensor may not be available. 187 private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF; 188 189 // True if we are collecting a brightness adjustment sample, along with some data 190 // for the initial state of the sample. 191 private boolean mBrightnessAdjustmentSamplePending; 192 private float mBrightnessAdjustmentSampleOldAdjustment; 193 private float mBrightnessAdjustmentSampleOldLux; 194 private int mBrightnessAdjustmentSampleOldBrightness; 195 private float mBrightnessAdjustmentSampleOldGamma; 196 197 public AutomaticBrightnessController(Callbacks callbacks, Looper looper, 198 SensorManager sensorManager, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime, 199 int brightnessMin, int brightnessMax, float dozeScaleFactor, 200 int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, 201 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, 202 int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma, 203 HysteresisLevels dynamicHysteresis) { 204 mCallbacks = callbacks; 205 mSensorManager = sensorManager; 206 mBrightnessMapper = mapper; 207 mScreenBrightnessRangeMinimum = brightnessMin; 208 mScreenBrightnessRangeMaximum = brightnessMax; 209 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; 210 mDozeScaleFactor = dozeScaleFactor; 211 mNormalLightSensorRate = lightSensorRate; 212 mInitialLightSensorRate = initialLightSensorRate; 213 mCurrentLightSensorRate = -1; 214 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig; 215 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig; 216 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig; 217 mAmbientLightHorizon = ambientLightHorizon; 218 mWeightingIntercept = ambientLightHorizon; 219 mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma; 220 mDynamicHysteresis = dynamicHysteresis; 221 222 mHandler = new AutomaticBrightnessHandler(looper); 223 mAmbientLightRingBuffer = 224 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon); 225 mInitialHorizonAmbientLightRingBuffer = 226 new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon); 227 228 if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) { 229 mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); 230 } 231 } 232 233 public int getAutomaticScreenBrightness() { 234 if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) { 235 return (int) (mScreenAutoBrightness * mDozeScaleFactor); 236 } 237 return mScreenAutoBrightness; 238 } 239 240 public float getAutomaticScreenBrightnessAdjustment() { 241 return mScreenAutoBrightnessAdjustment; 242 } 243 244 public void configure(boolean enable, @Nullable BrightnessConfiguration configuration, 245 float brightness, boolean userChangedBrightness, float adjustment, 246 boolean userChangedAutoBrightnessAdjustment, int displayPolicy) { 247 // While dozing, the application processor may be suspended which will prevent us from 248 // receiving new information from the light sensor. On some devices, we may be able to 249 // switch to a wake-up light sensor instead but for now we will simply disable the sensor 250 // and hold onto the last computed screen auto brightness. We save the dozing flag for 251 // debugging purposes. 252 boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE); 253 boolean changed = setBrightnessConfiguration(configuration); 254 changed |= setDisplayPolicy(displayPolicy); 255 changed |= setScreenAutoBrightnessAdjustment(adjustment); 256 if (userChangedBrightness && enable) { 257 // Update the brightness curve with the new user control point. It's critical this 258 // happens after we update the autobrightness adjustment since it may reset it. 259 changed |= setScreenBrightnessByUser(brightness); 260 } 261 final boolean userInitiatedChange = 262 userChangedBrightness || userChangedAutoBrightnessAdjustment; 263 if (userInitiatedChange && enable && !dozing) { 264 prepareBrightnessAdjustmentSample(); 265 } 266 changed |= setLightSensorEnabled(enable && !dozing); 267 if (changed) { 268 updateAutoBrightness(false /*sendUpdate*/); 269 } 270 } 271 272 public boolean hasUserDataPoints() { 273 return mBrightnessMapper.hasUserDataPoints(); 274 } 275 276 public boolean isDefaultConfig() { 277 return mBrightnessMapper.isDefaultConfig(); 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 " + mDisplayPolicy + " to " + policy); 288 } 289 if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) { 290 mHandler.sendEmptyMessageDelayed(MSG_RESET_SHORT_TERM_MODEL, 291 SHORT_TERM_MODEL_TIMEOUT_MILLIS); 292 } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) { 293 mHandler.removeMessages(MSG_RESET_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 // Reset the brightness adjustment so that the next time we're queried for brightness we 312 // return the value the user set. 313 mScreenAutoBrightnessAdjustment = 0.0f; 314 return true; 315 } 316 317 private void resetShortTermModel() { 318 mBrightnessMapper.clearUserDataPoints(); 319 } 320 321 public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) { 322 return mBrightnessMapper.setBrightnessConfiguration(configuration); 323 } 324 325 public void dump(PrintWriter pw) { 326 pw.println(); 327 pw.println("Automatic Brightness Controller Configuration:"); 328 pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); 329 pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); 330 pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); 331 pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig); 332 pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig); 333 pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig); 334 335 pw.println(); 336 pw.println("Automatic Brightness Controller State:"); 337 pw.println(" mLightSensor=" + mLightSensor); 338 pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); 339 pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime)); 340 pw.println(" mAmbientLux=" + mAmbientLux); 341 pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon); 342 pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold); 343 pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold); 344 pw.println(" mLastObservedLux=" + mLastObservedLux); 345 pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime)); 346 pw.println(" mRecentLightSamples=" + mRecentLightSamples); 347 pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer); 348 pw.println(" mInitialHorizonAmbientLightRingBuffer=" + 349 mInitialHorizonAmbientLightRingBuffer); 350 pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); 351 pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment); 352 pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" 353 + mScreenAutoBrightnessAdjustmentMaxGamma); 354 pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); 355 pw.println(" mDisplayPolicy=" + mDisplayPolicy); 356 357 pw.println(); 358 mBrightnessMapper.dump(pw); 359 } 360 361 private boolean setLightSensorEnabled(boolean enable) { 362 if (enable) { 363 if (!mLightSensorEnabled) { 364 mLightSensorEnabled = true; 365 mLightSensorEnableTime = SystemClock.uptimeMillis(); 366 mCurrentLightSensorRate = mInitialLightSensorRate; 367 mSensorManager.registerListener(mLightSensorListener, mLightSensor, 368 mCurrentLightSensorRate * 1000, mHandler); 369 return true; 370 } 371 } else { 372 if (mLightSensorEnabled) { 373 mLightSensorEnabled = false; 374 mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; 375 mRecentLightSamples = 0; 376 mAmbientLightRingBuffer.clear(); 377 mInitialHorizonAmbientLightRingBuffer.clear(); 378 mCurrentLightSensorRate = -1; 379 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); 380 mSensorManager.unregisterListener(mLightSensorListener); 381 } 382 } 383 return false; 384 } 385 386 private void handleLightSensorEvent(long time, float lux) { 387 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux); 388 mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); 389 390 if (mAmbientLightRingBuffer.size() == 0) { 391 // switch to using the steady-state sample rate after grabbing the initial light sample 392 adjustLightSensorRate(mNormalLightSensorRate); 393 } 394 applyLightSensorMeasurement(time, lux); 395 updateAmbientLux(time); 396 } 397 398 private void applyLightSensorMeasurement(long time, float lux) { 399 mRecentLightSamples++; 400 // Store all of the light measurements for the intial horizon period. This is to help 401 // diagnose dim wake ups and slow responses in b/27951906. 402 if (time <= mLightSensorEnableTime + mAmbientLightHorizon) { 403 mInitialHorizonAmbientLightRingBuffer.push(time, lux); 404 } 405 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon); 406 mAmbientLightRingBuffer.push(time, lux); 407 408 // Remember this sample value. 409 mLastObservedLux = lux; 410 mLastObservedLuxTime = time; 411 } 412 413 private void adjustLightSensorRate(int lightSensorRate) { 414 // if the light sensor rate changed, update the sensor listener 415 if (lightSensorRate != mCurrentLightSensorRate) { 416 if (DEBUG) { 417 Slog.d(TAG, "adjustLightSensorRate: 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 setScreenAutoBrightnessAdjustment(float adjustment) { 428 if (adjustment != mScreenAutoBrightnessAdjustment) { 429 mScreenAutoBrightnessAdjustment = adjustment; 430 return true; 431 } 432 return false; 433 } 434 435 private void setAmbientLux(float lux) { 436 if (DEBUG) { 437 Slog.d(TAG, "setAmbientLux(" + lux + ")"); 438 } 439 if (lux < 0) { 440 Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0."); 441 lux = 0; 442 } 443 mAmbientLux = lux; 444 mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux); 445 mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux); 446 } 447 448 private float calculateAmbientLux(long now, long horizon) { 449 if (DEBUG) { 450 Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")"); 451 } 452 final int N = mAmbientLightRingBuffer.size(); 453 if (N == 0) { 454 Slog.e(TAG, "calculateAmbientLux: No ambient light readings available"); 455 return -1; 456 } 457 458 // Find the first measurement that is just outside of the horizon. 459 int endIndex = 0; 460 final long horizonStartTime = now - horizon; 461 for (int i = 0; i < N-1; i++) { 462 if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) { 463 endIndex++; 464 } else { 465 break; 466 } 467 } 468 if (DEBUG) { 469 Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" 470 + mAmbientLightRingBuffer.getTime(endIndex) + ", " 471 + mAmbientLightRingBuffer.getLux(endIndex) 472 + ")"); 473 } 474 float sum = 0; 475 float totalWeight = 0; 476 long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS; 477 for (int i = N - 1; i >= endIndex; i--) { 478 long eventTime = mAmbientLightRingBuffer.getTime(i); 479 if (i == endIndex && eventTime < horizonStartTime) { 480 // If we're at the final value, make sure we only consider the part of the sample 481 // within our desired horizon. 482 eventTime = horizonStartTime; 483 } 484 final long startTime = eventTime - now; 485 float weight = calculateWeight(startTime, endTime); 486 float lux = mAmbientLightRingBuffer.getLux(i); 487 if (DEBUG) { 488 Slog.d(TAG, "calculateAmbientLux: [" + 489 (startTime) + ", " + 490 (endTime) + "]: lux=" + lux + ", weight=" + weight); 491 } 492 totalWeight += weight; 493 sum += mAmbientLightRingBuffer.getLux(i) * weight; 494 endTime = startTime; 495 } 496 if (DEBUG) { 497 Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight + 498 ", newAmbientLux=" + (sum / totalWeight)); 499 } 500 return sum / totalWeight; 501 } 502 503 private float calculateWeight(long startDelta, long endDelta) { 504 return weightIntegral(endDelta) - weightIntegral(startDelta); 505 } 506 507 // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the 508 // horizon we're looking at and provides a non-linear weighting for light samples. 509 private float weightIntegral(long x) { 510 return x * (x * 0.5f + mWeightingIntercept); 511 } 512 513 private long nextAmbientLightBrighteningTransition(long time) { 514 final int N = mAmbientLightRingBuffer.size(); 515 long earliestValidTime = time; 516 for (int i = N - 1; i >= 0; i--) { 517 if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) { 518 break; 519 } 520 earliestValidTime = mAmbientLightRingBuffer.getTime(i); 521 } 522 return earliestValidTime + mBrighteningLightDebounceConfig; 523 } 524 525 private long nextAmbientLightDarkeningTransition(long time) { 526 final int N = mAmbientLightRingBuffer.size(); 527 long earliestValidTime = time; 528 for (int i = N - 1; i >= 0; i--) { 529 if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) { 530 break; 531 } 532 earliestValidTime = mAmbientLightRingBuffer.getTime(i); 533 } 534 return earliestValidTime + mDarkeningLightDebounceConfig; 535 } 536 537 private void updateAmbientLux() { 538 long time = SystemClock.uptimeMillis(); 539 mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon); 540 updateAmbientLux(time); 541 } 542 543 private void updateAmbientLux(long time) { 544 // If the light sensor was just turned on then immediately update our initial 545 // estimate of the current ambient light level. 546 if (!mAmbientLuxValid) { 547 final long timeWhenSensorWarmedUp = 548 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime; 549 if (time < timeWhenSensorWarmedUp) { 550 if (DEBUG) { 551 Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " 552 + "time=" + time 553 + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp); 554 } 555 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, 556 timeWhenSensorWarmedUp); 557 return; 558 } 559 setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS)); 560 mAmbientLuxValid = true; 561 if (DEBUG) { 562 Slog.d(TAG, "updateAmbientLux: Initializing: " 563 + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer 564 + ", mAmbientLux=" + mAmbientLux); 565 } 566 updateAutoBrightness(true); 567 } 568 569 long nextBrightenTransition = nextAmbientLightBrighteningTransition(time); 570 long nextDarkenTransition = nextAmbientLightDarkeningTransition(time); 571 // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term 572 // change in lighting conditions, and a fast ambient lux to determine what the new 573 // brightness situation is since the slow lux can be quite slow to converge. 574 // 575 // Note that both values need to be checked for sufficient change before updating the 576 // proposed ambient light value since the slow value might be sufficiently far enough away 577 // from the fast value to cause a recalculation while its actually just converging on 578 // the fast value still. 579 float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS); 580 float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS); 581 582 if (slowAmbientLux >= mBrighteningLuxThreshold && 583 fastAmbientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time 584 || slowAmbientLux <= mDarkeningLuxThreshold 585 && fastAmbientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) { 586 setAmbientLux(fastAmbientLux); 587 if (DEBUG) { 588 Slog.d(TAG, "updateAmbientLux: " 589 + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " 590 + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold 591 + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer 592 + ", mAmbientLux=" + mAmbientLux); 593 } 594 updateAutoBrightness(true); 595 nextBrightenTransition = nextAmbientLightBrighteningTransition(time); 596 nextDarkenTransition = nextAmbientLightDarkeningTransition(time); 597 } 598 long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition); 599 // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't 600 // exceed the necessary threshold, then it's possible we'll get a transition time prior to 601 // now. Rather than continually checking to see whether the weighted lux exceeds the 602 // threshold, schedule an update for when we'd normally expect another light sample, which 603 // should be enough time to decide whether we should actually transition to the new 604 // weighted ambient lux or not. 605 nextTransitionTime = 606 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate; 607 if (DEBUG) { 608 Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " 609 + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime)); 610 } 611 mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime); 612 } 613 614 private void updateAutoBrightness(boolean sendUpdate) { 615 if (!mAmbientLuxValid) { 616 return; 617 } 618 619 float value = mBrightnessMapper.getBrightness(mAmbientLux); 620 float gamma = 1.0f; 621 622 if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT 623 && mScreenAutoBrightnessAdjustment != 0.0f) { 624 final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma, 625 Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment))); 626 gamma *= adjGamma; 627 if (DEBUG) { 628 Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma); 629 } 630 } 631 632 if (gamma != 1.0f) { 633 final float in = value; 634 value = MathUtils.pow(value, gamma); 635 if (DEBUG) { 636 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma 637 + ", in=" + in + ", out=" + value); 638 } 639 } 640 641 int newScreenAutoBrightness = 642 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); 643 if (mScreenAutoBrightness != newScreenAutoBrightness) { 644 if (DEBUG) { 645 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" 646 + mScreenAutoBrightness + ", newScreenAutoBrightness=" 647 + newScreenAutoBrightness); 648 } 649 650 mScreenAutoBrightness = newScreenAutoBrightness; 651 mLastScreenAutoBrightnessGamma = gamma; 652 if (sendUpdate) { 653 mCallbacks.updateBrightness(); 654 } 655 } 656 } 657 658 private int clampScreenBrightness(int value) { 659 return MathUtils.constrain(value, 660 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); 661 } 662 663 private void prepareBrightnessAdjustmentSample() { 664 if (!mBrightnessAdjustmentSamplePending) { 665 mBrightnessAdjustmentSamplePending = true; 666 mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment; 667 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1; 668 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness; 669 mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma; 670 } else { 671 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 672 } 673 674 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE, 675 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS); 676 } 677 678 private void cancelBrightnessAdjustmentSample() { 679 if (mBrightnessAdjustmentSamplePending) { 680 mBrightnessAdjustmentSamplePending = false; 681 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 682 } 683 } 684 685 private void collectBrightnessAdjustmentSample() { 686 if (mBrightnessAdjustmentSamplePending) { 687 mBrightnessAdjustmentSamplePending = false; 688 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) { 689 if (DEBUG) { 690 Slog.d(TAG, "Auto-brightness adjustment changed by user: " 691 + "adj=" + mScreenAutoBrightnessAdjustment 692 + ", lux=" + mAmbientLux 693 + ", brightness=" + mScreenAutoBrightness 694 + ", gamma=" + mLastScreenAutoBrightnessGamma 695 + ", ring=" + mAmbientLightRingBuffer); 696 } 697 698 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ, 699 mBrightnessAdjustmentSampleOldAdjustment, 700 mBrightnessAdjustmentSampleOldLux, 701 mBrightnessAdjustmentSampleOldBrightness, 702 mBrightnessAdjustmentSampleOldGamma, 703 mScreenAutoBrightnessAdjustment, 704 mAmbientLux, 705 mScreenAutoBrightness, 706 mLastScreenAutoBrightnessGamma); 707 } 708 } 709 } 710 711 private final class AutomaticBrightnessHandler extends Handler { 712 public AutomaticBrightnessHandler(Looper looper) { 713 super(looper, null, true /*async*/); 714 } 715 716 @Override 717 public void handleMessage(Message msg) { 718 switch (msg.what) { 719 case MSG_UPDATE_AMBIENT_LUX: 720 updateAmbientLux(); 721 break; 722 723 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE: 724 collectBrightnessAdjustmentSample(); 725 break; 726 727 case MSG_RESET_SHORT_TERM_MODEL: 728 resetShortTermModel(); 729 break; 730 } 731 } 732 } 733 734 private final SensorEventListener mLightSensorListener = new SensorEventListener() { 735 @Override 736 public void onSensorChanged(SensorEvent event) { 737 if (mLightSensorEnabled) { 738 final long time = SystemClock.uptimeMillis(); 739 final float lux = event.values[0]; 740 handleLightSensorEvent(time, lux); 741 } 742 } 743 744 @Override 745 public void onAccuracyChanged(Sensor sensor, int accuracy) { 746 // Not used. 747 } 748 }; 749 750 /** Callbacks to request updates to the display's power state. */ 751 interface Callbacks { 752 void updateBrightness(); 753 } 754 755 /** 756 * A ring buffer of ambient light measurements sorted by time. 757 * 758 * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted 759 * from oldest to newest. 760 */ 761 private static final class AmbientLightRingBuffer { 762 // Proportional extra capacity of the buffer beyond the expected number of light samples 763 // in the horizon 764 private static final float BUFFER_SLACK = 1.5f; 765 private float[] mRingLux; 766 private long[] mRingTime; 767 private int mCapacity; 768 769 // The first valid element and the next open slot. 770 // Note that if mCount is zero then there are no valid elements. 771 private int mStart; 772 private int mEnd; 773 private int mCount; 774 775 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) { 776 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate); 777 mRingLux = new float[mCapacity]; 778 mRingTime = new long[mCapacity]; 779 } 780 781 public float getLux(int index) { 782 return mRingLux[offsetOf(index)]; 783 } 784 785 public long getTime(int index) { 786 return mRingTime[offsetOf(index)]; 787 } 788 789 public void push(long time, float lux) { 790 int next = mEnd; 791 if (mCount == mCapacity) { 792 int newSize = mCapacity * 2; 793 794 float[] newRingLux = new float[newSize]; 795 long[] newRingTime = new long[newSize]; 796 int length = mCapacity - mStart; 797 System.arraycopy(mRingLux, mStart, newRingLux, 0, length); 798 System.arraycopy(mRingTime, mStart, newRingTime, 0, length); 799 if (mStart != 0) { 800 System.arraycopy(mRingLux, 0, newRingLux, length, mStart); 801 System.arraycopy(mRingTime, 0, newRingTime, length, mStart); 802 } 803 mRingLux = newRingLux; 804 mRingTime = newRingTime; 805 806 next = mCapacity; 807 mCapacity = newSize; 808 mStart = 0; 809 } 810 mRingTime[next] = time; 811 mRingLux[next] = lux; 812 mEnd = next + 1; 813 if (mEnd == mCapacity) { 814 mEnd = 0; 815 } 816 mCount++; 817 } 818 819 public void prune(long horizon) { 820 if (mCount == 0) { 821 return; 822 } 823 824 while (mCount > 1) { 825 int next = mStart + 1; 826 if (next >= mCapacity) { 827 next -= mCapacity; 828 } 829 if (mRingTime[next] > horizon) { 830 // Some light sensors only produce data upon a change in the ambient light 831 // levels, so we need to consider the previous measurement as the ambient light 832 // level for all points in time up until we receive a new measurement. Thus, we 833 // always want to keep the youngest element that would be removed from the 834 // buffer and just set its measurement time to the horizon time since at that 835 // point it is the ambient light level, and to remove it would be to drop a 836 // valid data point within our horizon. 837 break; 838 } 839 mStart = next; 840 mCount -= 1; 841 } 842 843 if (mRingTime[mStart] < horizon) { 844 mRingTime[mStart] = horizon; 845 } 846 } 847 848 public int size() { 849 return mCount; 850 } 851 852 public void clear() { 853 mStart = 0; 854 mEnd = 0; 855 mCount = 0; 856 } 857 858 @Override 859 public String toString() { 860 StringBuffer buf = new StringBuffer(); 861 buf.append('['); 862 for (int i = 0; i < mCount; i++) { 863 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis(); 864 if (i != 0) { 865 buf.append(", "); 866 } 867 buf.append(getLux(i)); 868 buf.append(" / "); 869 buf.append(next - getTime(i)); 870 buf.append("ms"); 871 } 872 buf.append(']'); 873 return buf.toString(); 874 } 875 876 private int offsetOf(int index) { 877 if (index >= mCount || index < 0) { 878 throw new ArrayIndexOutOfBoundsException(index); 879 } 880 index += mStart; 881 if (index >= mCapacity) { 882 index -= mCapacity; 883 } 884 return index; 885 } 886 } 887} 888