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