summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Wright <michaelwr@google.com>2014-02-13 00:46:58 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-02-13 00:46:59 +0000
commit111d6fef3a362bc9fd4fa61c49bccc9079521161 (patch)
tree069ff276db9e52fd5ea949ce2c72d3184608de84
parentc1d388fa0ae72b8171fb64b8dc2a43116db5c983 (diff)
parent639c8becc6d0efe9c205f3abf1e9347464a95020 (diff)
downloadframeworks_base-111d6fef3a362bc9fd4fa61c49bccc9079521161.zip
frameworks_base-111d6fef3a362bc9fd4fa61c49bccc9079521161.tar.gz
frameworks_base-111d6fef3a362bc9fd4fa61c49bccc9079521161.tar.bz2
Merge changes I8f4249da,I012bd99f
* changes: Extract automatic brightness strategy New ambient light level filtering for automatic brightness
-rw-r--r--services/core/java/com/android/server/power/AutomaticBrightnessController.java687
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerController.java574
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerRequest.java9
3 files changed, 766 insertions, 504 deletions
diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
new file mode 100644
index 0000000..b24c572
--- /dev/null
+++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.server.twilight.TwilightListener;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.util.MathUtils;
+import android.util.Spline;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+class AutomaticBrightnessController {
+ private static final String TAG = "AutomaticBrightnessController";
+
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+
+ // If true, enables the use of the screen auto-brightness adjustment setting.
+ private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
+ PowerManager.useScreenAutoBrightnessAdjustmentFeature();
+
+ // The maximum range of gamma adjustment possible using the screen
+ // auto-brightness adjustment setting.
+ private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
+
+ // Light sensor event rate in milliseconds.
+ private static final int LIGHT_SENSOR_RATE_MILLIS = 1000;
+
+ // Period of time in which to consider light samples in milliseconds.
+ private static final int AMBIENT_LIGHT_HORIZON = 10000;
+
+ // Stability requirements in milliseconds for accepting a new brightness level. This is used
+ // for debouncing the light sensor. Different constants are used to debounce the light sensor
+ // when adapting to brighter or darker environments. This parameter controls how quickly
+ // brightness changes occur in response to an observed change in light level that exceeds the
+ // hysteresis threshold.
+ private static final long BRIGHTENING_LIGHT_DEBOUNCE = 4000;
+ private static final long DARKENING_LIGHT_DEBOUNCE = 8000;
+
+ // Hysteresis constraints for brightening or darkening.
+ // The recent lux must have changed by at least this fraction relative to the
+ // current ambient lux before a change will be considered.
+ private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
+ private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
+
+ // The intercept used for the weighting calculation. This is used in order to keep all possible
+ // weighting values positive.
+ private static final int WEIGHTING_INTERCEPT = AMBIENT_LIGHT_HORIZON;
+
+ // How long the current sensor reading is assumed to be valid beyond the current time.
+ // This provides a bit of prediction, as well as ensures that the weight for the last sample is
+ // non-zero, which in turn ensures that the total weight is non-zero.
+ private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
+
+ // If true, enables the use of the current time as an auto-brightness adjustment.
+ // The basic idea here is to expand the dynamic range of auto-brightness
+ // when it is especially dark outside. The light sensor tends to perform
+ // poorly at low light levels so we compensate for it by making an
+ // assumption about the environment.
+ private static final boolean USE_TWILIGHT_ADJUSTMENT =
+ PowerManager.useTwilightAdjustmentFeature();
+
+ // Specifies the maximum magnitude of the time of day adjustment.
+ private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f;
+
+ // The amount of time after or before sunrise over which to start adjusting
+ // the gamma. We want the change to happen gradually so that it is below the
+ // threshold of perceptibility and so that the adjustment has maximum effect
+ // well after dusk.
+ private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
+
+ private static final int MSG_UPDATE_AMBIENT_LUX = 1;
+
+ // Callbacks for requesting updates to the the display's power state
+ private final Callbacks mCallbacks;
+
+ // The sensor manager.
+ private final SensorManager mSensorManager;
+
+ // The light sensor, or null if not available or needed.
+ private final Sensor mLightSensor;
+
+ // The twilight service.
+ private final TwilightManager mTwilight;
+
+ // The auto-brightness spline adjustment.
+ // The brightness values have been scaled to a range of 0..1.
+ private final Spline mScreenAutoBrightnessSpline;
+
+ // The minimum and maximum screen brightnesses.
+ private final int mScreenBrightnessRangeMinimum;
+ private final int mScreenBrightnessRangeMaximum;
+
+ // Amount of time to delay auto-brightness after screen on while waiting for
+ // the light sensor to warm-up in milliseconds.
+ // May be 0 if no warm-up is required.
+ private int mLightSensorWarmUpTimeConfig;
+
+ // Set to true if the light sensor is enabled.
+ private boolean mLightSensorEnabled;
+
+ // The time when the light sensor was enabled.
+ private long mLightSensorEnableTime;
+
+ // The currently accepted nominal ambient light level.
+ private float mAmbientLux;
+
+ // True if mAmbientLux holds a valid value.
+ private boolean mAmbientLuxValid;
+
+ // The ambient light level threshold at which to brighten or darken the screen.
+ private float mBrighteningLuxThreshold;
+ private float mDarkeningLuxThreshold;
+
+ // The most recent light sample.
+ private float mLastObservedLux;
+
+ // The time of the most light recent sample.
+ private long mLastObservedLuxTime;
+
+ // The number of light samples collected since the light sensor was enabled.
+ private int mRecentLightSamples;
+
+ // A ring buffer containing all of the recent ambient light sensor readings.
+ private AmbientLightRingBuffer mAmbientLightRingBuffer;
+
+ // The handler
+ private AutomaticBrightnessHandler mHandler;
+
+ // The screen brightness level that has been chosen by the auto-brightness
+ // algorithm. The actual brightness should ramp towards this value.
+ // We preserve this value even when we stop using the light sensor so
+ // that we can quickly revert to the previous auto-brightness level
+ // while the light sensor warms up.
+ // Use -1 if there is no current auto-brightness value available.
+ private int mScreenAutoBrightness = -1;
+
+ // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
+ private float mScreenAutoBrightnessAdjustment = 0.0f;
+
+ // The last screen auto-brightness gamma. (For printing in dump() only.)
+ private float mLastScreenAutoBrightnessGamma = 1.0f;
+
+ public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+ TwilightManager twilight, SensorManager sensorManager, Spline autoBrightnessSpline,
+ int lightSensorWarmUpTime, int brightnessMin, int brightnessMax) {
+ mCallbacks = callbacks;
+ mTwilight = twilight;
+ mSensorManager = sensorManager;
+ mScreenAutoBrightnessSpline = autoBrightnessSpline;
+ mScreenBrightnessRangeMinimum = brightnessMin;
+ mScreenBrightnessRangeMaximum = brightnessMax;
+ mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
+
+ mHandler = new AutomaticBrightnessHandler(looper);
+ mAmbientLightRingBuffer = new AmbientLightRingBuffer();
+
+ if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ }
+
+ if (USE_TWILIGHT_ADJUSTMENT) {
+ mTwilight.registerListener(mTwilightListener, mHandler);
+ }
+ }
+
+ public int getAutomaticScreenBrightness() {
+ return mScreenAutoBrightness;
+ }
+
+ public void updatePowerState(DisplayPowerRequest request) {
+ if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
+ || setLightSensorEnabled(request.useAutoBrightness
+ && DisplayPowerRequest.wantScreenOn(request.screenState))) {
+ updateAutoBrightness(false /*sendUpdate*/);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("Automatic Brightness Controller Configuration:");
+ pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
+ pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+ pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+
+ pw.println();
+ pw.println("Automatic Brightness Controller State:");
+ pw.println(" mLightSensor=" + mLightSensor);
+ pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
+ pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
+ pw.println(" mLastObservedLux=" + mLastObservedLux);
+ pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+ pw.println(" mRecentLightSamples=" + mRecentLightSamples);
+ pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+ pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
+ pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
+ pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
+ }
+
+ private boolean setLightSensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mLightSensorEnabled) {
+ mLightSensorEnabled = true;
+ mLightSensorEnableTime = SystemClock.uptimeMillis();
+ mSensorManager.registerListener(mLightSensorListener, mLightSensor,
+ LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
+ return true;
+ }
+ } else {
+ if (mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mAmbientLuxValid = false;
+ mRecentLightSamples = 0;
+ mAmbientLightRingBuffer.clear();
+ mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ mSensorManager.unregisterListener(mLightSensorListener);
+ }
+ }
+ return false;
+ }
+
+ private void handleLightSensorEvent(long time, float lux) {
+ mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+
+ applyLightSensorMeasurement(time, lux);
+ updateAmbientLux(time);
+ }
+
+ private void applyLightSensorMeasurement(long time, float lux) {
+ mRecentLightSamples++;
+ mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.push(time, lux);
+
+ // Remember this sample value.
+ mLastObservedLux = lux;
+ mLastObservedLuxTime = time;
+ }
+
+ private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
+ if (adjustment != mScreenAutoBrightnessAdjustment) {
+ mScreenAutoBrightnessAdjustment = adjustment;
+ return true;
+ }
+ return false;
+ }
+
+ private void setAmbientLux(float lux) {
+ mAmbientLux = lux;
+ mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
+ mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
+ }
+
+ private float calculateAmbientLux(long now) {
+ final int N = mAmbientLightRingBuffer.size();
+ if (N == 0) {
+ Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
+ return -1;
+ }
+ float sum = 0;
+ float totalWeight = 0;
+ long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
+ for (int i = N - 1; i >= 0; i--) {
+ long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
+ float weight = calculateWeight(startTime, endTime);
+ float lux = mAmbientLightRingBuffer.getLux(i);
+ if (DEBUG) {
+ Slog.d(TAG, "calculateAmbientLux: [" +
+ (startTime) + ", " +
+ (endTime) + "]: lux=" + lux + ", weight=" + weight);
+ }
+ totalWeight += weight;
+ sum += mAmbientLightRingBuffer.getLux(i) * weight;
+ endTime = startTime;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
+ ", newAmbientLux=" + (sum / totalWeight));
+ }
+ return sum / totalWeight;
+ }
+
+ private static float calculateWeight(long startDelta, long endDelta) {
+ return weightIntegral(endDelta) - weightIntegral(startDelta);
+ }
+
+ // Evaluates the integral of y = x + WEIGHTING_INTERCEPT. This is always positive for the
+ // horizon we're looking at and provides a non-linear weighting for light samples.
+ private static float weightIntegral(long x) {
+ return x * (x * 0.5f + WEIGHTING_INTERCEPT);
+ }
+
+ private long nextAmbientLightBrighteningTransition(long time) {
+ final int N = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = N - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + BRIGHTENING_LIGHT_DEBOUNCE;
+ }
+
+ private long nextAmbientLightDarkeningTransition(long time) {
+ final int N = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = N - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + DARKENING_LIGHT_DEBOUNCE;
+ }
+
+ private void updateAmbientLux() {
+ long time = SystemClock.uptimeMillis();
+ mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ updateAmbientLux(time);
+ }
+
+ private void updateAmbientLux(long time) {
+ // If the light sensor was just turned on then immediately update our initial
+ // estimate of the current ambient light level.
+ if (!mAmbientLuxValid) {
+ final long timeWhenSensorWarmedUp =
+ mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
+ if (time < timeWhenSensorWarmedUp) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
+ + "time=" + time
+ + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
+ }
+ mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
+ timeWhenSensorWarmedUp);
+ return;
+ }
+ setAmbientLux(calculateAmbientLux(time));
+ mAmbientLuxValid = true;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Initializing: "
+ + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ + ", mAmbientLux=" + mAmbientLux);
+ }
+ updateAutoBrightness(true);
+ }
+
+ long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ float ambientLux = calculateAmbientLux(time);
+
+ if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
+ || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
+ setAmbientLux(ambientLux);
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: "
+ + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
+ + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ + ", mAmbientLux=" + mAmbientLux);
+ }
+ updateAutoBrightness(true);
+ nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ }
+ long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
+ // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
+ // exceed the necessary threshold, then it's possible we'll get a transition time prior to
+ // now. Rather than continually checking to see whether the weighted lux exceeds the
+ // threshold, schedule an update for when we'd normally expect another light sample, which
+ // should be enough time to decide whether we should actually transition to the new
+ // weighted ambient lux or not.
+ nextTransitionTime =
+ nextTransitionTime > time ? nextTransitionTime : time + LIGHT_SENSOR_RATE_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
+ + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
+ }
+ mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
+ }
+
+ private void updateAutoBrightness(boolean sendUpdate) {
+ if (!mAmbientLuxValid) {
+ return;
+ }
+
+ float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
+ float gamma = 1.0f;
+
+ if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
+ && mScreenAutoBrightnessAdjustment != 0.0f) {
+ final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
+ Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
+ gamma *= adjGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
+ }
+ }
+
+ if (USE_TWILIGHT_ADJUSTMENT) {
+ TwilightState state = mTwilight.getCurrentState();
+ if (state != null && state.isNight()) {
+ final long now = System.currentTimeMillis();
+ final float earlyGamma =
+ getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
+ final float lateGamma =
+ getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
+ gamma *= earlyGamma * lateGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
+ + ", lateGamma=" + lateGamma);
+ }
+ }
+ }
+
+ if (gamma != 1.0f) {
+ final float in = value;
+ value = MathUtils.pow(value, gamma);
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
+ + ", in=" + in + ", out=" + value);
+ }
+ }
+
+ int newScreenAutoBrightness =
+ clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+ if (mScreenAutoBrightness != newScreenAutoBrightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
+ + mScreenAutoBrightness + ", newScreenAutoBrightness="
+ + newScreenAutoBrightness);
+ }
+
+ mScreenAutoBrightness = newScreenAutoBrightness;
+ mLastScreenAutoBrightnessGamma = gamma;
+ if (sendUpdate) {
+ mCallbacks.updateBrightness();
+ }
+ }
+ }
+
+ private int clampScreenBrightness(int value) {
+ return MathUtils.constrain(value,
+ mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
+ }
+
+ private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
+ if (lastSunset < 0 || nextSunrise < 0
+ || now < lastSunset || now > nextSunrise) {
+ return 1.0f;
+ }
+
+ if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
+ }
+
+ private final class AutomaticBrightnessHandler extends Handler {
+ public AutomaticBrightnessHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_AMBIENT_LUX:
+ updateAmbientLux();
+ break;
+ }
+ }
+ }
+
+ private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mLightSensorEnabled) {
+ final long time = SystemClock.uptimeMillis();
+ final float lux = event.values[0];
+ handleLightSensorEvent(time, lux);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ private final TwilightListener mTwilightListener = new TwilightListener() {
+ @Override
+ public void onTwilightStateChanged() {
+ updateAutoBrightness(true /*sendUpdate*/);
+ }
+ };
+
+ /** Callbacks to request updates to the display's power state. */
+ interface Callbacks {
+ void updateBrightness();
+ }
+
+ private static final class AmbientLightRingBuffer{
+ // Proportional extra capacity of the buffer beyond the expected number of light samples
+ // in the horizon
+ private static final float BUFFER_SLACK = 1.5f;
+ private static final int DEFAULT_CAPACITY =
+ (int) Math.ceil(AMBIENT_LIGHT_HORIZON * BUFFER_SLACK / LIGHT_SENSOR_RATE_MILLIS);
+ private float[] mRingLux;
+ private long[] mRingTime;
+ private int mCapacity;
+
+ // The first valid element and the next open slot.
+ // Note that if mCount is zero then there are no valid elements.
+ private int mStart;
+ private int mEnd;
+ private int mCount;
+
+ public AmbientLightRingBuffer() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ public AmbientLightRingBuffer(int initialCapacity) {
+ mCapacity = initialCapacity;
+ mRingLux = new float[mCapacity];
+ mRingTime = new long[mCapacity];
+ }
+
+ public float getLux(int index) {
+ return mRingLux[offsetOf(index)];
+ }
+
+ public long getTime(int index) {
+ return mRingTime[offsetOf(index)];
+ }
+
+ public void push(long time, float lux) {
+ int next = mEnd;
+ if (mCount == mCapacity) {
+ int newSize = mCapacity * 2;
+
+ float[] newRingLux = new float[newSize];
+ long[] newRingTime = new long[newSize];
+ int length = mCapacity - mStart;
+ System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
+ System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
+ if (mStart != 0) {
+ System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
+ System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
+ }
+ mRingLux = newRingLux;
+ mRingTime = newRingTime;
+
+ next = mCapacity;
+ mCapacity = newSize;
+ mStart = 0;
+ }
+ mRingTime[next] = time;
+ mRingLux[next] = lux;
+ mEnd = next + 1;
+ if (mEnd == mCapacity) {
+ mEnd = 0;
+ }
+ mCount++;
+ }
+
+ public void prune(long horizon) {
+ if (mCount == 0) {
+ return;
+ }
+
+ while (mCount > 1) {
+ int next = mStart + 1;
+ if (next >= mCapacity) {
+ next -= mCapacity;
+ }
+ if (mRingTime[next] > horizon) {
+ // Some light sensors only produce data upon a change in the ambient light
+ // levels, so we need to consider the previous measurement as the ambient light
+ // level for all points in time up until we receive a new measurement. Thus, we
+ // always want to keep the youngest element that would be removed from the
+ // buffer and just set its measurement time to the horizon time since at that
+ // point it is the ambient light level, and to remove it would be to drop a
+ // valid data point within our horizon.
+ break;
+ }
+ mStart = next;
+ mCount -= 1;
+ }
+
+ if (mRingTime[mStart] < horizon) {
+ mRingTime[mStart] = horizon;
+ }
+ }
+
+ public int size() {
+ return mCount;
+ }
+
+ public boolean isEmpty() {
+ return mCount == 0;
+ }
+
+ public void clear() {
+ mStart = 0;
+ mEnd = 0;
+ mCount = 0;
+ }
+
+ @Override
+ public String toString() {
+ final int length = mCapacity - mStart;
+ float[] lux = new float[mCount];
+ long[] time = new long[mCount];
+
+ if (mCount <= length) {
+ System.arraycopy(mRingLux, mStart, lux, 0, mCount);
+ System.arraycopy(mRingTime, mStart, time, 0, mCount);
+ } else {
+ System.arraycopy(mRingLux, mStart, lux, 0, length);
+ System.arraycopy(mRingLux, 0, lux, length, mCount - length);
+
+ System.arraycopy(mRingTime, mStart, time, 0, length);
+ System.arraycopy(mRingTime, 0, time, length, mCount - length);
+ }
+ return "AmbientLightRingBuffer{mCapacity=" + mCapacity
+ + ", mStart=" + mStart
+ + ", mEnd=" + mEnd
+ + ", mCount=" + mCount
+ + ", mRingLux=" + Arrays.toString(lux)
+ + ", mRingTime=" + Arrays.toString(time)
+ + "}";
+ }
+
+ private int offsetOf(int index) {
+ if (index >= mCount || index < 0) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ index += mStart;
+ if (index >= mCapacity) {
+ index -= mCapacity;
+ }
+ return index;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/DisplayPowerController.java b/services/core/java/com/android/server/power/DisplayPowerController.java
index b63f625..291bb64 100644
--- a/services/core/java/com/android/server/power/DisplayPowerController.java
+++ b/services/core/java/com/android/server/power/DisplayPowerController.java
@@ -35,7 +35,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.text.format.DateUtils;
-import android.util.FloatMath;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.Spline;
import android.util.TimeUtils;
@@ -64,12 +64,11 @@ import java.io.PrintWriter;
* For debugging, you can make the electron beam and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
-final class DisplayPowerController {
+final class DisplayPowerController implements AutomaticBrightnessController.Callbacks {
private static final String TAG = "DisplayPowerController";
private static boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
- private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, uses the electron beam on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
@@ -77,13 +76,6 @@ final class DisplayPowerController {
// screen state returns. Playing the animation can also be somewhat slow.
private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
- // If true, enables the use of the screen auto-brightness adjustment setting.
- private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
- PowerManager.useScreenAutoBrightnessAdjustmentFeature();
-
- // The maximum range of gamma adjustment possible using the screen
- // auto-brightness adjustment setting.
- private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
// The minimum reduction in brightness when dimmed.
private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
@@ -110,7 +102,6 @@ final class DisplayPowerController {
private static final int MSG_UPDATE_POWER_STATE = 1;
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
- private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -123,41 +114,10 @@ final class DisplayPowerController {
// Trigger proximity if distance is less than 5 cm.
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
- // Light sensor event rate in milliseconds.
- private static final int LIGHT_SENSOR_RATE_MILLIS = 1000;
-
- // A rate for generating synthetic light sensor events in the case where the light
- // sensor hasn't reported any new data in a while and we need it to update the
- // debounce filter. We only synthesize light sensor measurements when needed.
- private static final int SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS =
- LIGHT_SENSOR_RATE_MILLIS * 2;
-
// Brightness animation ramp rate in brightness units per second.
private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
- // IIR filter time constants in milliseconds for computing two moving averages of
- // the light samples. One is a long-term average and the other is a short-term average.
- // We can use these filters to assess trends in ambient brightness.
- // The short term average gives us a filtered but relatively low latency measurement.
- // The long term average informs us about the overall trend.
- private static final long SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 1000;
- private static final long LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 5000;
-
- // Stability requirements in milliseconds for accepting a new brightness
- // level. This is used for debouncing the light sensor. Different constants
- // are used to debounce the light sensor when adapting to brighter or darker environments.
- // This parameter controls how quickly brightness changes occur in response to
- // an observed change in light level that exceeds the hysteresis threshold.
- private static final long BRIGHTENING_LIGHT_DEBOUNCE = 4000;
- private static final long DARKENING_LIGHT_DEBOUNCE = 8000;
-
- // Hysteresis constraints for brightening or darkening.
- // The recent lux must have changed by at least this fraction relative to the
- // current ambient lux before a change will be considered.
- private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
- private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
-
private final Object mLock = new Object();
// Notifier for sending asynchronous notifications.
@@ -181,18 +141,12 @@ final class DisplayPowerController {
// The lights service.
private final LightsManager mLights;
- // The twilight service.
- private final TwilightManager mTwilight;
-
// The sensor manager.
private final SensorManager mSensorManager;
// The proximity sensor, or null if not available or needed.
private Sensor mProximitySensor;
- // The light sensor, or null if not available or needed.
- private Sensor mLightSensor;
-
// The dim screen brightness.
private final int mScreenBrightnessDimConfig;
@@ -205,15 +159,6 @@ final class DisplayPowerController {
// True if auto-brightness should be used.
private boolean mUseSoftwareAutoBrightnessConfig;
- // The auto-brightness spline adjustment.
- // The brightness values have been scaled to a range of 0..1.
- private Spline mScreenAutoBrightnessSpline;
-
- // Amount of time to delay auto-brightness after screen on while waiting for
- // the light sensor to warm-up in milliseconds.
- // May be 0 if no warm-up is required.
- private int mLightSensorWarmUpTimeConfig;
-
// True if we should fade the screen while turning it off, false if we should play
// a stylish electron beam animation instead.
private boolean mElectronBeamFadesConfig;
@@ -283,67 +228,18 @@ final class DisplayPowerController {
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
- // Set to true if the light sensor is enabled.
- private boolean mLightSensorEnabled;
-
- // The time when the light sensor was enabled.
- private long mLightSensorEnableTime;
-
- // The currently accepted nominal ambient light level.
- private float mAmbientLux;
-
- // True if mAmbientLux holds a valid value.
- private boolean mAmbientLuxValid;
-
- // The ambient light level threshold at which to brighten or darken the screen.
- private float mBrighteningLuxThreshold;
- private float mDarkeningLuxThreshold;
-
- // The most recent light sample.
- private float mLastObservedLux;
-
- // The time of the most light recent sample.
- private long mLastObservedLuxTime;
-
- // The number of light samples collected since the light sensor was enabled.
- private int mRecentLightSamples;
-
- // The long-term and short-term filtered light measurements.
- private float mRecentShortTermAverageLux;
- private float mRecentLongTermAverageLux;
-
- // The direction in which the average lux is moving relative to the current ambient lux.
- // 0 if not changing or within hysteresis threshold.
- // 1 if brightening beyond hysteresis threshold.
- // -1 if darkening beyond hysteresis threshold.
- private int mDebounceLuxDirection;
-
- // The time when the average lux last changed direction.
- private long mDebounceLuxTime;
-
- // The screen brightness level that has been chosen by the auto-brightness
- // algorithm. The actual brightness should ramp towards this value.
- // We preserve this value even when we stop using the light sensor so
- // that we can quickly revert to the previous auto-brightness level
- // while the light sensor warms up.
- // Use -1 if there is no current auto-brightness value available.
- private int mScreenAutoBrightness = -1;
-
- // The last screen auto-brightness gamma. (For printing in dump() only.)
- private float mLastScreenAutoBrightnessGamma = 1.0f;
-
// True if the screen auto-brightness value is actually being used to
// set the display brightness.
private boolean mUsingScreenAutoBrightness;
+ // The controller for the automatic brightness level.
+ private AutomaticBrightnessController mAutomaticBrightnessController;
+
// Animators.
private ObjectAnimator mElectronBeamOnAnimator;
private ObjectAnimator mElectronBeamOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- // Twilight changed. We might recalculate auto-brightness values.
- private boolean mTwilightChanged;
-
/**
* Creates the display power controller.
*/
@@ -359,7 +255,6 @@ final class DisplayPowerController {
mCallbackHandler = callbackHandler;
mLights = lights;
- mTwilight = twilight;
mSensorManager = sensorManager;
final Resources resources = context.getResources();
@@ -367,9 +262,11 @@ final class DisplayPowerController {
mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessDim));
- int screenBrightnessMinimum = Math.min(resources.getInteger(
+ int screenBrightnessRangeMinimum = clampAbsoluteBrightness(Math.min(resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMinimum),
- mScreenBrightnessDimConfig);
+ mScreenBrightnessDimConfig));
+
+ mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
@@ -378,9 +275,11 @@ final class DisplayPowerController {
com.android.internal.R.array.config_autoBrightnessLevels);
int[] screenBrightness = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ int lightSensorWarmUpTimeConfig = resources.getInteger(
+ com.android.internal.R.integer.config_lightSensorWarmupTime);
- mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
- if (mScreenAutoBrightnessSpline == null) {
+ Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+ if (screenAutoBrightnessSpline == null) {
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
+ "(size " + screenBrightness.length + ") "
+ "must be monotic and have exactly one more entry than "
@@ -389,17 +288,17 @@ final class DisplayPowerController {
+ "Auto-brightness will be disabled.");
mUseSoftwareAutoBrightnessConfig = false;
} else {
- if (screenBrightness[0] < screenBrightnessMinimum) {
- screenBrightnessMinimum = screenBrightness[0];
+ if (screenBrightness[0] < screenBrightnessRangeMinimum) {
+ screenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightness[0]);
}
+ mAutomaticBrightnessController = new AutomaticBrightnessController(this, looper,
+ twilight, sensorManager, screenAutoBrightnessSpline,
+ lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
+ mScreenBrightnessRangeMaximum);
}
-
- mLightSensorWarmUpTimeConfig = resources.getInteger(
- com.android.internal.R.integer.config_lightSensorWarmupTime);
}
- mScreenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightnessMinimum);
- mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
+ mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;
mElectronBeamFadesConfig = resources.getBoolean(
com.android.internal.R.bool.config_animateScreenLights);
@@ -412,39 +311,6 @@ final class DisplayPowerController {
}
}
- if (mUseSoftwareAutoBrightnessConfig
- && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
- mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
- }
-
- if (mUseSoftwareAutoBrightnessConfig && USE_TWILIGHT_ADJUSTMENT) {
- mTwilight.registerListener(mTwilightListener, mHandler);
- }
- }
-
- private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
- try {
- final int n = brightness.length;
- float[] x = new float[n];
- float[] y = new float[n];
- y[0] = normalizeAbsoluteBrightness(brightness[0]);
- for (int i = 1; i < n; i++) {
- x[i] = lux[i - 1];
- y[i] = normalizeAbsoluteBrightness(brightness[i]);
- }
-
- Spline spline = Spline.createMonotoneCubicSpline(x, y);
- if (DEBUG) {
- Slog.d(TAG, "Auto-brightness spline: " + spline);
- for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
- Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
- }
- }
- return spline;
- } catch (IllegalArgumentException ex) {
- Slog.e(TAG, "Could not create auto-brightness spline.", ex);
- return null;
- }
}
/**
@@ -566,9 +432,7 @@ final class DisplayPowerController {
// Update the power state request.
final boolean mustNotify;
boolean mustInitialize = false;
- boolean updateAutoBrightness = mTwilightChanged;
boolean wasDim = false;
- mTwilightChanged = false;
synchronized (mLock) {
mPendingUpdatePowerStateLocked = false;
@@ -583,10 +447,6 @@ final class DisplayPowerController {
mPendingRequestChangedLocked = false;
mustInitialize = true;
} else if (mPendingRequestChangedLocked) {
- if (mPowerRequest.screenAutoBrightnessAdjustment
- != mPendingRequestLocked.screenAutoBrightnessAdjustment) {
- updateAutoBrightness = true;
- }
wasDim = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM);
mPowerRequest.copyFrom(mPendingRequestLocked);
mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
@@ -633,18 +493,19 @@ final class DisplayPowerController {
}
// Turn on the light sensor if needed.
- if (mLightSensor != null) {
- setLightSensorEnabled(mPowerRequest.useAutoBrightness
- && wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.updatePowerState(mPowerRequest);
}
// Set the screen brightness.
- if (wantScreenOn(mPowerRequest.screenState)) {
+ if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) {
int target;
boolean slow;
- if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
+ int screenAutoBrightness = mAutomaticBrightnessController != null ?
+ mAutomaticBrightnessController.getAutomaticScreenBrightness() : -1;
+ if (screenAutoBrightness >= 0) {
// Use current auto-brightness value.
- target = mScreenAutoBrightness;
+ target = screenAutoBrightness;
slow = mUsingScreenAutoBrightness;
mUsingScreenAutoBrightness = true;
} else {
@@ -674,7 +535,7 @@ final class DisplayPowerController {
// Animate the screen on or off.
if (!mScreenOffBecauseOfProximity) {
- if (wantScreenOn(mPowerRequest.screenState)) {
+ if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) {
// Want screen on.
// Wait for previous off animation to complete beforehand.
// It is relatively short but if we cancel it and switch to the
@@ -751,6 +612,11 @@ final class DisplayPowerController {
}
}
+ @Override
+ public void updateBrightness() {
+ sendUpdatePowerState();
+ }
+
private void blockScreenOn() {
if (!mScreenOnWasBlocked) {
mScreenOnWasBlocked = true;
@@ -783,25 +649,8 @@ final class DisplayPowerController {
}
private int clampScreenBrightness(int value) {
- return clamp(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
- }
-
- private static int clampAbsoluteBrightness(int value) {
- return clamp(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
- }
-
- private static int clamp(int value, int min, int max) {
- if (value <= min) {
- return min;
- }
- if (value >= max) {
- return max;
- }
- return value;
- }
-
- private static float normalizeAbsoluteBrightness(int value) {
- return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
+ return MathUtils.constrain(
+ value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
}
private void animateScreenBrightness(int target, int rate) {
@@ -902,270 +751,6 @@ final class DisplayPowerController {
mPendingProximityDebounceTime = debounceTime;
}
- private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) {
- if (enable) {
- if (!mLightSensorEnabled) {
- updateAutoBrightness = true;
- mLightSensorEnabled = true;
- mLightSensorEnableTime = SystemClock.uptimeMillis();
- mSensorManager.registerListener(mLightSensorListener, mLightSensor,
- LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
- }
- } else {
- if (mLightSensorEnabled) {
- mLightSensorEnabled = false;
- mAmbientLuxValid = false;
- mRecentLightSamples = 0;
- mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
- mSensorManager.unregisterListener(mLightSensorListener);
- }
- }
- if (updateAutoBrightness) {
- updateAutoBrightness(false);
- }
- }
-
- private void handleLightSensorEvent(long time, float lux) {
- mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
-
- applyLightSensorMeasurement(time, lux);
- updateAmbientLux(time);
- }
-
- private void applyLightSensorMeasurement(long time, float lux) {
- // Update our filters.
- mRecentLightSamples += 1;
- if (mRecentLightSamples == 1) {
- mRecentShortTermAverageLux = lux;
- mRecentLongTermAverageLux = lux;
- } else {
- final long timeDelta = time - mLastObservedLuxTime;
- mRecentShortTermAverageLux += (lux - mRecentShortTermAverageLux)
- * timeDelta / (SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
- mRecentLongTermAverageLux += (lux - mRecentLongTermAverageLux)
- * timeDelta / (LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
- }
-
- // Remember this sample value.
- mLastObservedLux = lux;
- mLastObservedLuxTime = time;
- }
-
- private void setAmbientLux(float lux) {
- mAmbientLux = lux;
- mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
- mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
- }
-
- private void updateAmbientLux(long time) {
- // If the light sensor was just turned on then immediately update our initial
- // estimate of the current ambient light level.
- if (!mAmbientLuxValid) {
- final long timeWhenSensorWarmedUp =
- mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
- if (time < timeWhenSensorWarmedUp) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
- timeWhenSensorWarmedUp);
- return;
- }
- setAmbientLux(mRecentShortTermAverageLux);
- mAmbientLuxValid = true;
- mDebounceLuxDirection = 0;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Initializing: "
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mRecentShortTermAverageLux > mBrighteningLuxThreshold
- && mRecentLongTermAverageLux > mBrighteningLuxThreshold) {
- // The ambient environment appears to be brightening.
- if (mDebounceLuxDirection <= 0) {
- mDebounceLuxDirection = 1;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Possibly brightened, waiting for "
- + BRIGHTENING_LIGHT_DEBOUNCE + " ms: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
- long debounceTime = mDebounceLuxTime + BRIGHTENING_LIGHT_DEBOUNCE;
- if (time < debounceTime) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
- return;
- }
- setAmbientLux(mRecentShortTermAverageLux);
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Brightened: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mRecentShortTermAverageLux < mDarkeningLuxThreshold
- && mRecentLongTermAverageLux < mDarkeningLuxThreshold) {
- // The ambient environment appears to be darkening.
- if (mDebounceLuxDirection >= 0) {
- mDebounceLuxDirection = -1;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Possibly darkened, waiting for "
- + DARKENING_LIGHT_DEBOUNCE + " ms: "
- + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
- long debounceTime = mDebounceLuxTime + DARKENING_LIGHT_DEBOUNCE;
- if (time < debounceTime) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
- return;
- }
- // Be conservative about reducing the brightness, only reduce it a little bit
- // at a time to avoid having to bump it up again soon.
- setAmbientLux(Math.max(mRecentShortTermAverageLux, mRecentLongTermAverageLux));
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Darkened: "
- + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mDebounceLuxDirection != 0) {
- // No change or change is within the hysteresis thresholds.
- mDebounceLuxDirection = 0;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Canceled debounce: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
-
- // Now that we've done all of that, we haven't yet posted a debounce
- // message. So consider the case where current lux is beyond the
- // threshold. It's possible that the light sensor may not report values
- // if the light level does not change, so we need to occasionally
- // synthesize sensor readings in order to make sure the brightness is
- // adjusted accordingly. Note these thresholds may have changed since
- // we entered the function because we called setAmbientLux and
- // updateAutoBrightness along the way.
- if (mLastObservedLux > mBrighteningLuxThreshold
- || mLastObservedLux < mDarkeningLuxThreshold) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
- time + SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS);
- }
- }
-
- private void debounceLightSensor() {
- if (mLightSensorEnabled) {
- long time = SystemClock.uptimeMillis();
- if (time >= mLastObservedLuxTime + SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS) {
- if (DEBUG) {
- Slog.d(TAG, "debounceLightSensor: Synthesizing light sensor measurement "
- + "after " + (time - mLastObservedLuxTime) + " ms.");
- }
- applyLightSensorMeasurement(time, mLastObservedLux);
- }
- updateAmbientLux(time);
- }
- }
-
- private void updateAutoBrightness(boolean sendUpdate) {
- if (!mAmbientLuxValid) {
- return;
- }
-
- float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
- float gamma = 1.0f;
-
- if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
- && mPowerRequest.screenAutoBrightnessAdjustment != 0.0f) {
- final float adjGamma = FloatMath.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
- Math.min(1.0f, Math.max(-1.0f,
- -mPowerRequest.screenAutoBrightnessAdjustment)));
- gamma *= adjGamma;
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
- }
- }
-
- if (USE_TWILIGHT_ADJUSTMENT) {
- TwilightState state = mTwilight.getCurrentState();
- if (state != null && state.isNight()) {
- final long now = System.currentTimeMillis();
- final float earlyGamma =
- getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
- final float lateGamma =
- getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
- gamma *= earlyGamma * lateGamma;
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
- + ", lateGamma=" + lateGamma);
- }
- }
- }
-
- if (gamma != 1.0f) {
- final float in = value;
- value = FloatMath.pow(value, gamma);
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
- + ", in=" + in + ", out=" + value);
- }
- }
-
- int newScreenAutoBrightness = clampScreenBrightness(
- Math.round(value * PowerManager.BRIGHTNESS_ON));
- if (mScreenAutoBrightness != newScreenAutoBrightness) {
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
- + mScreenAutoBrightness + ", newScreenAutoBrightness="
- + newScreenAutoBrightness);
- }
-
- mScreenAutoBrightness = newScreenAutoBrightness;
- mLastScreenAutoBrightnessGamma = gamma;
- if (sendUpdate) {
- sendUpdatePowerState();
- }
- }
- }
-
- private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
- if (lastSunset < 0 || nextSunrise < 0
- || now < lastSunset || now > nextSunrise) {
- return 1.0f;
- }
-
- if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
- return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
- (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
- }
-
- if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
- return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
- (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
- }
-
- return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
- }
-
- private static float lerp(float x, float y, float alpha) {
- return x + (y - x) * alpha;
- }
-
private void sendOnStateChangedWithWakelock() {
mDisplaySuspendBlocker.acquire();
mCallbackHandler.post(mOnStateChangedRunnable);
@@ -1224,8 +809,6 @@ final class DisplayPowerController {
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mUseSoftwareAutoBrightnessConfig="
+ mUseSoftwareAutoBrightnessConfig);
- pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
- pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
mHandler.runWithScissors(new Runnable() {
@Override
@@ -1249,25 +832,7 @@ final class DisplayPowerController {
pw.println(" mPendingProximityDebounceTime="
+ TimeUtils.formatUptime(mPendingProximityDebounceTime));
pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
-
- pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
- pw.println(" mLightSensorEnableTime="
- + TimeUtils.formatUptime(mLightSensorEnableTime));
- pw.println(" mAmbientLux=" + mAmbientLux);
- pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mLastObservedLux=" + mLastObservedLux);
- pw.println(" mLastObservedLuxTime="
- + TimeUtils.formatUptime(mLastObservedLuxTime));
- pw.println(" mRecentLightSamples=" + mRecentLightSamples);
- pw.println(" mRecentShortTermAverageLux=" + mRecentShortTermAverageLux);
- pw.println(" mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
- pw.println(" mDebounceLuxDirection=" + mDebounceLuxDirection);
- pw.println(" mDebounceLuxTime=" + TimeUtils.formatUptime(mDebounceLuxTime));
- pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
- pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
- pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
if (mElectronBeamOnAnimator != null) {
pw.println(" mElectronBeamOnAnimator.isStarted()=" +
@@ -1281,6 +846,11 @@ final class DisplayPowerController {
if (mPowerState != null) {
mPowerState.dump(pw);
}
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ }
+
}
private static String proximityToString(int state) {
@@ -1296,13 +866,37 @@ final class DisplayPowerController {
}
}
- private static boolean wantScreenOn(int state) {
- switch (state) {
- case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
- case DisplayPowerRequest.SCREEN_STATE_DIM:
- return true;
+ private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ try {
+ final int n = brightness.length;
+ float[] x = new float[n];
+ float[] y = new float[n];
+ y[0] = normalizeAbsoluteBrightness(brightness[0]);
+ for (int i = 1; i < n; i++) {
+ x[i] = lux[i - 1];
+ y[i] = normalizeAbsoluteBrightness(brightness[i]);
+ }
+
+ Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ if (DEBUG) {
+ Slog.d(TAG, "Auto-brightness spline: " + spline);
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
+ }
+ }
+ return spline;
+ } catch (IllegalArgumentException ex) {
+ Slog.e(TAG, "Could not create auto-brightness spline.", ex);
+ return null;
}
- return false;
+ }
+
+ private static float normalizeAbsoluteBrightness(int value) {
+ return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
+ }
+
+ private static int clampAbsoluteBrightness(int value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
/**
@@ -1329,10 +923,6 @@ final class DisplayPowerController {
case MSG_PROXIMITY_SENSOR_DEBOUNCED:
debounceProximitySensor();
break;
-
- case MSG_LIGHT_SENSOR_DEBOUNCED:
- debounceLightSensor();
- break;
}
}
}
@@ -1353,28 +943,4 @@ final class DisplayPowerController {
// Not used.
}
};
-
- private final SensorEventListener mLightSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mLightSensorEnabled) {
- final long time = SystemClock.uptimeMillis();
- final float lux = event.values[0];
- handleLightSensorEvent(time, lux);
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Not used.
- }
- };
-
- private final TwilightListener mTwilightListener = new TwilightListener() {
- @Override
- public void onTwilightStateChanged() {
- mTwilightChanged = true;
- updatePowerState();
- }
- };
}
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequest.java b/services/core/java/com/android/server/power/DisplayPowerRequest.java
index 22f17d7..7626f76 100644
--- a/services/core/java/com/android/server/power/DisplayPowerRequest.java
+++ b/services/core/java/com/android/server/power/DisplayPowerRequest.java
@@ -114,4 +114,13 @@ final class DisplayPowerRequest {
+ ", useAutoBrightness=" + useAutoBrightness
+ ", blockScreenOn=" + blockScreenOn;
}
+
+ public static boolean wantScreenOn(int state) {
+ switch(state) {
+ case SCREEN_STATE_DIM:
+ case SCREEN_STATE_BRIGHT:
+ return true;
+ }
+ return false;
+ }
}