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