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