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