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