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