diff options
author | Michael Wright <michaelwr@google.com> | 2014-02-13 00:46:58 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-02-13 00:46:59 +0000 |
commit | 111d6fef3a362bc9fd4fa61c49bccc9079521161 (patch) | |
tree | 069ff276db9e52fd5ea949ce2c72d3184608de84 | |
parent | c1d388fa0ae72b8171fb64b8dc2a43116db5c983 (diff) | |
parent | 639c8becc6d0efe9c205f3abf1e9347464a95020 (diff) | |
download | frameworks_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
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; + } } |