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