AutomaticBrightnessController.java revision 428aed01e1e2923968027dff57132d8d8d5c4905
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.annotation.Nullable; 26import android.hardware.Sensor; 27import android.hardware.SensorEvent; 28import android.hardware.SensorEventListener; 29import android.hardware.SensorManager; 30import android.os.Handler; 31import android.os.Looper; 32import android.os.Message; 33import android.os.PowerManager; 34import android.os.SystemClock; 35import android.text.format.DateUtils; 36import android.util.EventLog; 37import android.util.MathUtils; 38import android.util.Spline; 39import android.util.Slog; 40import android.util.TimeUtils; 41 42import java.io.PrintWriter; 43 44class AutomaticBrightnessController { 45 private static final String TAG = "AutomaticBrightnessController"; 46 47 private static final boolean DEBUG = false; 48 private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; 49 50 // If true, enables the use of the screen auto-brightness adjustment setting. 51 private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true; 52 53 // How long the current sensor reading is assumed to be valid beyond the current time. 54 // This provides a bit of prediction, as well as ensures that the weight for the last sample is 55 // non-zero, which in turn ensures that the total weight is non-zero. 56 private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100; 57 58 // Specifies the maximum magnitude of the time of day adjustment. 59 private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f; 60 61 // Debounce for sampling user-initiated changes in display brightness to ensure 62 // the user is satisfied with the result before storing the sample. 63 private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000; 64 65 private static final int MSG_UPDATE_AMBIENT_LUX = 1; 66 private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2; 67 68 // Callbacks for requesting updates to the display's power state 69 private final Callbacks mCallbacks; 70 71 // The sensor manager. 72 private final SensorManager mSensorManager; 73 74 // The light sensor, or null if not available or needed. 75 private final Sensor mLightSensor; 76 77 // The twilight service. 78 private final TwilightManager mTwilight; 79 80 // The auto-brightness spline adjustment. 81 // The brightness values have been scaled to a range of 0..1. 82 private final Spline mScreenAutoBrightnessSpline; 83 84 // The minimum and maximum screen brightnesses. 85 private final int mScreenBrightnessRangeMinimum; 86 private final int mScreenBrightnessRangeMaximum; 87 private final float mDozeScaleFactor; 88 89 // Light sensor event rate in milliseconds. 90 private final int mLightSensorRate; 91 92 // Stability requirements in milliseconds for accepting a new brightness level. This is used 93 // for debouncing the light sensor. Different constants are used to debounce the light sensor 94 // when adapting to brighter or darker environments. This parameter controls how quickly 95 // brightness changes occur in response to an observed change in light level that exceeds the 96 // hysteresis threshold. 97 private final long mBrighteningLightDebounceConfig; 98 private final long mDarkeningLightDebounceConfig; 99 100 // If true immediately after the screen is turned on the controller will try to adjust the 101 // brightness based on the current sensor reads. If false, the controller will collect more data 102 // and only then decide whether to change brightness. 103 private final boolean mResetAmbientLuxAfterWarmUpConfig; 104 105 // Period of time in which to consider light samples in milliseconds. 106 private final int mAmbientLightHorizon; 107 108 // The intercept used for the weighting calculation. This is used in order to keep all possible 109 // weighting values positive. 110 private final int mWeightingIntercept; 111 112 // accessor object for determining thresholds to change brightness dynamically 113 private final HysteresisLevels mDynamicHysteresis; 114 115 // Amount of time to delay auto-brightness after screen on while waiting for 116 // the light sensor to warm-up in milliseconds. 117 // May be 0 if no warm-up is required. 118 private int mLightSensorWarmUpTimeConfig; 119 120 // Set to true if the light sensor is enabled. 121 private boolean mLightSensorEnabled; 122 123 // The time when the light sensor was enabled. 124 private long mLightSensorEnableTime; 125 126 // The currently accepted nominal ambient light level. 127 private float mAmbientLux; 128 129 // True if mAmbientLux holds a valid value. 130 private boolean mAmbientLuxValid; 131 132 // The ambient light level threshold at which to brighten or darken the screen. 133 private float mBrighteningLuxThreshold; 134 private float mDarkeningLuxThreshold; 135 136 // The most recent light sample. 137 private float mLastObservedLux; 138 139 // The time of the most light recent sample. 140 private long mLastObservedLuxTime; 141 142 // The number of light samples collected since the light sensor was enabled. 143 private int mRecentLightSamples; 144 145 // A ring buffer containing all of the recent ambient light sensor readings. 146 private AmbientLightRingBuffer mAmbientLightRingBuffer; 147 148 // A ring buffer containing the light sensor readings for the initial horizon period. 149 private AmbientLightRingBuffer mInitialHorizonAmbientLightRingBuffer; 150 151 // The handler 152 private AutomaticBrightnessHandler mHandler; 153 154 // The screen brightness level that has been chosen by the auto-brightness 155 // algorithm. The actual brightness should ramp towards this value. 156 // We preserve this value even when we stop using the light sensor so 157 // that we can quickly revert to the previous auto-brightness level 158 // while the light sensor warms up. 159 // Use -1 if there is no current auto-brightness value available. 160 private int mScreenAutoBrightness = -1; 161 162 // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter) 163 private float mScreenAutoBrightnessAdjustment = 0.0f; 164 165 // The maximum range of gamma adjustment possible using the screen 166 // auto-brightness adjustment setting. 167 private float mScreenAutoBrightnessAdjustmentMaxGamma; 168 169 // The last screen auto-brightness gamma. (For printing in dump() only.) 170 private float mLastScreenAutoBrightnessGamma = 1.0f; 171 172 // Are we going to adjust brightness while dozing. 173 private boolean mDozing; 174 175 // True if we are collecting a brightness adjustment sample, along with some data 176 // for the initial state of the sample. 177 private boolean mBrightnessAdjustmentSamplePending; 178 private float mBrightnessAdjustmentSampleOldAdjustment; 179 private float mBrightnessAdjustmentSampleOldLux; 180 private int mBrightnessAdjustmentSampleOldBrightness; 181 private float mBrightnessAdjustmentSampleOldGamma; 182 183 private boolean mUseTwilight; 184 185 public AutomaticBrightnessController(Callbacks callbacks, Looper looper, 186 SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime, 187 int brightnessMin, int brightnessMax, float dozeScaleFactor, 188 int lightSensorRate, long brighteningLightDebounceConfig, 189 long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, 190 int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma, 191 HysteresisLevels dynamicHysteresis) { 192 mCallbacks = callbacks; 193 mTwilight = LocalServices.getService(TwilightManager.class); 194 mSensorManager = sensorManager; 195 mScreenAutoBrightnessSpline = autoBrightnessSpline; 196 mScreenBrightnessRangeMinimum = brightnessMin; 197 mScreenBrightnessRangeMaximum = brightnessMax; 198 mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; 199 mDozeScaleFactor = dozeScaleFactor; 200 mLightSensorRate = lightSensorRate; 201 mBrighteningLightDebounceConfig = brighteningLightDebounceConfig; 202 mDarkeningLightDebounceConfig = darkeningLightDebounceConfig; 203 mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig; 204 mAmbientLightHorizon = ambientLightHorizon; 205 mWeightingIntercept = ambientLightHorizon; 206 mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma; 207 mDynamicHysteresis = dynamicHysteresis; 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.getLastTwilightState()=" + mTwilight.getLastTwilightState()); 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 = mDynamicHysteresis.getBrighteningThreshold(lux); 347 mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux); 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.getLastTwilightState(); 499 if (state != null && state.isNight()) { 500 final long duration = state.sunriseTimeMillis() - state.sunsetTimeMillis(); 501 final long progress = System.currentTimeMillis() - state.sunsetTimeMillis(); 502 final float amount = (float) Math.pow(2.0 * progress / duration - 1.0, 2.0); 503 gamma *= 1 + amount * TWILIGHT_ADJUSTMENT_MAX_GAMMA; 504 if (DEBUG) { 505 Slog.d(TAG, "updateAutoBrightness: twilight amount=" + amount); 506 } 507 } 508 } 509 510 if (gamma != 1.0f) { 511 final float in = value; 512 value = MathUtils.pow(value, gamma); 513 if (DEBUG) { 514 Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma 515 + ", in=" + in + ", out=" + value); 516 } 517 } 518 519 int newScreenAutoBrightness = 520 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); 521 if (mScreenAutoBrightness != newScreenAutoBrightness) { 522 if (DEBUG) { 523 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" 524 + mScreenAutoBrightness + ", newScreenAutoBrightness=" 525 + newScreenAutoBrightness); 526 } 527 528 mScreenAutoBrightness = newScreenAutoBrightness; 529 mLastScreenAutoBrightnessGamma = gamma; 530 if (sendUpdate) { 531 mCallbacks.updateBrightness(); 532 } 533 } 534 } 535 536 private int clampScreenBrightness(int value) { 537 return MathUtils.constrain(value, 538 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); 539 } 540 541 private void prepareBrightnessAdjustmentSample() { 542 if (!mBrightnessAdjustmentSamplePending) { 543 mBrightnessAdjustmentSamplePending = true; 544 mBrightnessAdjustmentSampleOldAdjustment = mScreenAutoBrightnessAdjustment; 545 mBrightnessAdjustmentSampleOldLux = mAmbientLuxValid ? mAmbientLux : -1; 546 mBrightnessAdjustmentSampleOldBrightness = mScreenAutoBrightness; 547 mBrightnessAdjustmentSampleOldGamma = mLastScreenAutoBrightnessGamma; 548 } else { 549 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 550 } 551 552 mHandler.sendEmptyMessageDelayed(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE, 553 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS); 554 } 555 556 private void cancelBrightnessAdjustmentSample() { 557 if (mBrightnessAdjustmentSamplePending) { 558 mBrightnessAdjustmentSamplePending = false; 559 mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE); 560 } 561 } 562 563 private void collectBrightnessAdjustmentSample() { 564 if (mBrightnessAdjustmentSamplePending) { 565 mBrightnessAdjustmentSamplePending = false; 566 if (mAmbientLuxValid && mScreenAutoBrightness >= 0) { 567 if (DEBUG) { 568 Slog.d(TAG, "Auto-brightness adjustment changed by user: " 569 + "adj=" + mScreenAutoBrightnessAdjustment 570 + ", lux=" + mAmbientLux 571 + ", brightness=" + mScreenAutoBrightness 572 + ", gamma=" + mLastScreenAutoBrightnessGamma 573 + ", ring=" + mAmbientLightRingBuffer); 574 } 575 576 EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ, 577 mBrightnessAdjustmentSampleOldAdjustment, 578 mBrightnessAdjustmentSampleOldLux, 579 mBrightnessAdjustmentSampleOldBrightness, 580 mBrightnessAdjustmentSampleOldGamma, 581 mScreenAutoBrightnessAdjustment, 582 mAmbientLux, 583 mScreenAutoBrightness, 584 mLastScreenAutoBrightnessGamma); 585 } 586 } 587 } 588 589 private final class AutomaticBrightnessHandler extends Handler { 590 public AutomaticBrightnessHandler(Looper looper) { 591 super(looper, null, true /*async*/); 592 } 593 594 @Override 595 public void handleMessage(Message msg) { 596 switch (msg.what) { 597 case MSG_UPDATE_AMBIENT_LUX: 598 updateAmbientLux(); 599 break; 600 601 case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE: 602 collectBrightnessAdjustmentSample(); 603 break; 604 } 605 } 606 } 607 608 private final SensorEventListener mLightSensorListener = new SensorEventListener() { 609 @Override 610 public void onSensorChanged(SensorEvent event) { 611 if (mLightSensorEnabled) { 612 final long time = SystemClock.uptimeMillis(); 613 final float lux = event.values[0]; 614 handleLightSensorEvent(time, lux); 615 } 616 } 617 618 @Override 619 public void onAccuracyChanged(Sensor sensor, int accuracy) { 620 // Not used. 621 } 622 }; 623 624 private final TwilightListener mTwilightListener = new TwilightListener() { 625 @Override 626 public void onTwilightStateChanged(@Nullable TwilightState state) { 627 updateAutoBrightness(true /*sendUpdate*/); 628 } 629 }; 630 631 /** Callbacks to request updates to the display's power state. */ 632 interface Callbacks { 633 void updateBrightness(); 634 } 635 636 private static final class AmbientLightRingBuffer { 637 // Proportional extra capacity of the buffer beyond the expected number of light samples 638 // in the horizon 639 private static final float BUFFER_SLACK = 1.5f; 640 private float[] mRingLux; 641 private long[] mRingTime; 642 private int mCapacity; 643 644 // The first valid element and the next open slot. 645 // Note that if mCount is zero then there are no valid elements. 646 private int mStart; 647 private int mEnd; 648 private int mCount; 649 650 public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) { 651 mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate); 652 mRingLux = new float[mCapacity]; 653 mRingTime = new long[mCapacity]; 654 } 655 656 public float getLux(int index) { 657 return mRingLux[offsetOf(index)]; 658 } 659 660 public long getTime(int index) { 661 return mRingTime[offsetOf(index)]; 662 } 663 664 public void push(long time, float lux) { 665 int next = mEnd; 666 if (mCount == mCapacity) { 667 int newSize = mCapacity * 2; 668 669 float[] newRingLux = new float[newSize]; 670 long[] newRingTime = new long[newSize]; 671 int length = mCapacity - mStart; 672 System.arraycopy(mRingLux, mStart, newRingLux, 0, length); 673 System.arraycopy(mRingTime, mStart, newRingTime, 0, length); 674 if (mStart != 0) { 675 System.arraycopy(mRingLux, 0, newRingLux, length, mStart); 676 System.arraycopy(mRingTime, 0, newRingTime, length, mStart); 677 } 678 mRingLux = newRingLux; 679 mRingTime = newRingTime; 680 681 next = mCapacity; 682 mCapacity = newSize; 683 mStart = 0; 684 } 685 mRingTime[next] = time; 686 mRingLux[next] = lux; 687 mEnd = next + 1; 688 if (mEnd == mCapacity) { 689 mEnd = 0; 690 } 691 mCount++; 692 } 693 694 public void prune(long horizon) { 695 if (mCount == 0) { 696 return; 697 } 698 699 while (mCount > 1) { 700 int next = mStart + 1; 701 if (next >= mCapacity) { 702 next -= mCapacity; 703 } 704 if (mRingTime[next] > horizon) { 705 // Some light sensors only produce data upon a change in the ambient light 706 // levels, so we need to consider the previous measurement as the ambient light 707 // level for all points in time up until we receive a new measurement. Thus, we 708 // always want to keep the youngest element that would be removed from the 709 // buffer and just set its measurement time to the horizon time since at that 710 // point it is the ambient light level, and to remove it would be to drop a 711 // valid data point within our horizon. 712 break; 713 } 714 mStart = next; 715 mCount -= 1; 716 } 717 718 if (mRingTime[mStart] < horizon) { 719 mRingTime[mStart] = horizon; 720 } 721 } 722 723 public int size() { 724 return mCount; 725 } 726 727 public void clear() { 728 mStart = 0; 729 mEnd = 0; 730 mCount = 0; 731 } 732 733 @Override 734 public String toString() { 735 StringBuffer buf = new StringBuffer(); 736 buf.append('['); 737 for (int i = 0; i < mCount; i++) { 738 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis(); 739 if (i != 0) { 740 buf.append(", "); 741 } 742 buf.append(getLux(i)); 743 buf.append(" / "); 744 buf.append(next - getTime(i)); 745 buf.append("ms"); 746 } 747 buf.append(']'); 748 return buf.toString(); 749 } 750 751 private int offsetOf(int index) { 752 if (index >= mCount || index < 0) { 753 throw new ArrayIndexOutOfBoundsException(index); 754 } 755 index += mStart; 756 if (index >= mCapacity) { 757 index -= mCapacity; 758 } 759 return index; 760 } 761 } 762} 763