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