Merge changes I8f4249da,I012bd99f
* changes: Extract automatic brightness strategy New ambient light level filtering for automatic brightness
This commit is contained in:
committed by
Android (Google) Code Review
commit
111d6fef3a
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ import android.os.Message;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.util.FloatMath;
|
import android.util.MathUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.Spline;
|
import android.util.Spline;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
@@ -64,12 +64,11 @@ import java.io.PrintWriter;
|
|||||||
* For debugging, you can make the electron beam and brightness animations run
|
* For debugging, you can make the electron beam and brightness animations run
|
||||||
* slower by changing the "animator duration scale" option in Development Settings.
|
* 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 final String TAG = "DisplayPowerController";
|
||||||
|
|
||||||
private static boolean DEBUG = false;
|
private static boolean DEBUG = false;
|
||||||
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = 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.
|
// If true, uses the electron beam on animation.
|
||||||
// We might want to turn this off if we cannot get a guarantee that the screen
|
// 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.
|
// screen state returns. Playing the animation can also be somewhat slow.
|
||||||
private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
|
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.
|
// The minimum reduction in brightness when dimmed.
|
||||||
private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
|
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_UPDATE_POWER_STATE = 1;
|
||||||
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
|
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_UNKNOWN = -1;
|
||||||
private static final int PROXIMITY_NEGATIVE = 0;
|
private static final int PROXIMITY_NEGATIVE = 0;
|
||||||
@@ -123,41 +114,10 @@ final class DisplayPowerController {
|
|||||||
// Trigger proximity if distance is less than 5 cm.
|
// Trigger proximity if distance is less than 5 cm.
|
||||||
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
|
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.
|
// 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_FAST = 200;
|
||||||
private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
|
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();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
// Notifier for sending asynchronous notifications.
|
// Notifier for sending asynchronous notifications.
|
||||||
@@ -181,18 +141,12 @@ final class DisplayPowerController {
|
|||||||
// The lights service.
|
// The lights service.
|
||||||
private final LightsManager mLights;
|
private final LightsManager mLights;
|
||||||
|
|
||||||
// The twilight service.
|
|
||||||
private final TwilightManager mTwilight;
|
|
||||||
|
|
||||||
// The sensor manager.
|
// The sensor manager.
|
||||||
private final SensorManager mSensorManager;
|
private final SensorManager mSensorManager;
|
||||||
|
|
||||||
// The proximity sensor, or null if not available or needed.
|
// The proximity sensor, or null if not available or needed.
|
||||||
private Sensor mProximitySensor;
|
private Sensor mProximitySensor;
|
||||||
|
|
||||||
// The light sensor, or null if not available or needed.
|
|
||||||
private Sensor mLightSensor;
|
|
||||||
|
|
||||||
// The dim screen brightness.
|
// The dim screen brightness.
|
||||||
private final int mScreenBrightnessDimConfig;
|
private final int mScreenBrightnessDimConfig;
|
||||||
|
|
||||||
@@ -205,15 +159,6 @@ final class DisplayPowerController {
|
|||||||
// True if auto-brightness should be used.
|
// True if auto-brightness should be used.
|
||||||
private boolean mUseSoftwareAutoBrightnessConfig;
|
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
|
// True if we should fade the screen while turning it off, false if we should play
|
||||||
// a stylish electron beam animation instead.
|
// a stylish electron beam animation instead.
|
||||||
private boolean mElectronBeamFadesConfig;
|
private boolean mElectronBeamFadesConfig;
|
||||||
@@ -283,67 +228,18 @@ final class DisplayPowerController {
|
|||||||
// The elapsed real time when the screen on was blocked.
|
// The elapsed real time when the screen on was blocked.
|
||||||
private long mScreenOnBlockStartRealTime;
|
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
|
// True if the screen auto-brightness value is actually being used to
|
||||||
// set the display brightness.
|
// set the display brightness.
|
||||||
private boolean mUsingScreenAutoBrightness;
|
private boolean mUsingScreenAutoBrightness;
|
||||||
|
|
||||||
|
// The controller for the automatic brightness level.
|
||||||
|
private AutomaticBrightnessController mAutomaticBrightnessController;
|
||||||
|
|
||||||
// Animators.
|
// Animators.
|
||||||
private ObjectAnimator mElectronBeamOnAnimator;
|
private ObjectAnimator mElectronBeamOnAnimator;
|
||||||
private ObjectAnimator mElectronBeamOffAnimator;
|
private ObjectAnimator mElectronBeamOffAnimator;
|
||||||
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
|
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
|
||||||
|
|
||||||
// Twilight changed. We might recalculate auto-brightness values.
|
|
||||||
private boolean mTwilightChanged;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the display power controller.
|
* Creates the display power controller.
|
||||||
*/
|
*/
|
||||||
@@ -359,7 +255,6 @@ final class DisplayPowerController {
|
|||||||
mCallbackHandler = callbackHandler;
|
mCallbackHandler = callbackHandler;
|
||||||
|
|
||||||
mLights = lights;
|
mLights = lights;
|
||||||
mTwilight = twilight;
|
|
||||||
mSensorManager = sensorManager;
|
mSensorManager = sensorManager;
|
||||||
|
|
||||||
final Resources resources = context.getResources();
|
final Resources resources = context.getResources();
|
||||||
@@ -367,9 +262,11 @@ final class DisplayPowerController {
|
|||||||
mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
|
mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
|
||||||
com.android.internal.R.integer.config_screenBrightnessDim));
|
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),
|
com.android.internal.R.integer.config_screenBrightnessSettingMinimum),
|
||||||
mScreenBrightnessDimConfig);
|
mScreenBrightnessDimConfig));
|
||||||
|
|
||||||
|
mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
|
||||||
|
|
||||||
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
|
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
|
||||||
com.android.internal.R.bool.config_automatic_brightness_available);
|
com.android.internal.R.bool.config_automatic_brightness_available);
|
||||||
@@ -378,9 +275,11 @@ final class DisplayPowerController {
|
|||||||
com.android.internal.R.array.config_autoBrightnessLevels);
|
com.android.internal.R.array.config_autoBrightnessLevels);
|
||||||
int[] screenBrightness = resources.getIntArray(
|
int[] screenBrightness = resources.getIntArray(
|
||||||
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
|
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
|
||||||
|
int lightSensorWarmUpTimeConfig = resources.getInteger(
|
||||||
|
com.android.internal.R.integer.config_lightSensorWarmupTime);
|
||||||
|
|
||||||
mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
|
Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
|
||||||
if (mScreenAutoBrightnessSpline == null) {
|
if (screenAutoBrightnessSpline == null) {
|
||||||
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
|
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
|
||||||
+ "(size " + screenBrightness.length + ") "
|
+ "(size " + screenBrightness.length + ") "
|
||||||
+ "must be monotic and have exactly one more entry than "
|
+ "must be monotic and have exactly one more entry than "
|
||||||
@@ -389,17 +288,17 @@ final class DisplayPowerController {
|
|||||||
+ "Auto-brightness will be disabled.");
|
+ "Auto-brightness will be disabled.");
|
||||||
mUseSoftwareAutoBrightnessConfig = false;
|
mUseSoftwareAutoBrightnessConfig = false;
|
||||||
} else {
|
} else {
|
||||||
if (screenBrightness[0] < screenBrightnessMinimum) {
|
if (screenBrightness[0] < screenBrightnessRangeMinimum) {
|
||||||
screenBrightnessMinimum = screenBrightness[0];
|
screenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightness[0]);
|
||||||
|
}
|
||||||
|
mAutomaticBrightnessController = new AutomaticBrightnessController(this, looper,
|
||||||
|
twilight, sensorManager, screenAutoBrightnessSpline,
|
||||||
|
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
|
||||||
|
mScreenBrightnessRangeMaximum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLightSensorWarmUpTimeConfig = resources.getInteger(
|
mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;
|
||||||
com.android.internal.R.integer.config_lightSensorWarmupTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
mScreenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightnessMinimum);
|
|
||||||
mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
|
|
||||||
|
|
||||||
mElectronBeamFadesConfig = resources.getBoolean(
|
mElectronBeamFadesConfig = resources.getBoolean(
|
||||||
com.android.internal.R.bool.config_animateScreenLights);
|
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.
|
// Update the power state request.
|
||||||
final boolean mustNotify;
|
final boolean mustNotify;
|
||||||
boolean mustInitialize = false;
|
boolean mustInitialize = false;
|
||||||
boolean updateAutoBrightness = mTwilightChanged;
|
|
||||||
boolean wasDim = false;
|
boolean wasDim = false;
|
||||||
mTwilightChanged = false;
|
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mPendingUpdatePowerStateLocked = false;
|
mPendingUpdatePowerStateLocked = false;
|
||||||
@@ -583,10 +447,6 @@ final class DisplayPowerController {
|
|||||||
mPendingRequestChangedLocked = false;
|
mPendingRequestChangedLocked = false;
|
||||||
mustInitialize = true;
|
mustInitialize = true;
|
||||||
} else if (mPendingRequestChangedLocked) {
|
} else if (mPendingRequestChangedLocked) {
|
||||||
if (mPowerRequest.screenAutoBrightnessAdjustment
|
|
||||||
!= mPendingRequestLocked.screenAutoBrightnessAdjustment) {
|
|
||||||
updateAutoBrightness = true;
|
|
||||||
}
|
|
||||||
wasDim = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM);
|
wasDim = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM);
|
||||||
mPowerRequest.copyFrom(mPendingRequestLocked);
|
mPowerRequest.copyFrom(mPendingRequestLocked);
|
||||||
mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
|
mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
|
||||||
@@ -633,18 +493,19 @@ final class DisplayPowerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Turn on the light sensor if needed.
|
// Turn on the light sensor if needed.
|
||||||
if (mLightSensor != null) {
|
if (mAutomaticBrightnessController != null) {
|
||||||
setLightSensorEnabled(mPowerRequest.useAutoBrightness
|
mAutomaticBrightnessController.updatePowerState(mPowerRequest);
|
||||||
&& wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the screen brightness.
|
// Set the screen brightness.
|
||||||
if (wantScreenOn(mPowerRequest.screenState)) {
|
if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) {
|
||||||
int target;
|
int target;
|
||||||
boolean slow;
|
boolean slow;
|
||||||
if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
|
int screenAutoBrightness = mAutomaticBrightnessController != null ?
|
||||||
|
mAutomaticBrightnessController.getAutomaticScreenBrightness() : -1;
|
||||||
|
if (screenAutoBrightness >= 0) {
|
||||||
// Use current auto-brightness value.
|
// Use current auto-brightness value.
|
||||||
target = mScreenAutoBrightness;
|
target = screenAutoBrightness;
|
||||||
slow = mUsingScreenAutoBrightness;
|
slow = mUsingScreenAutoBrightness;
|
||||||
mUsingScreenAutoBrightness = true;
|
mUsingScreenAutoBrightness = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -674,7 +535,7 @@ final class DisplayPowerController {
|
|||||||
|
|
||||||
// Animate the screen on or off.
|
// Animate the screen on or off.
|
||||||
if (!mScreenOffBecauseOfProximity) {
|
if (!mScreenOffBecauseOfProximity) {
|
||||||
if (wantScreenOn(mPowerRequest.screenState)) {
|
if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) {
|
||||||
// Want screen on.
|
// Want screen on.
|
||||||
// Wait for previous off animation to complete beforehand.
|
// Wait for previous off animation to complete beforehand.
|
||||||
// It is relatively short but if we cancel it and switch to the
|
// 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() {
|
private void blockScreenOn() {
|
||||||
if (!mScreenOnWasBlocked) {
|
if (!mScreenOnWasBlocked) {
|
||||||
mScreenOnWasBlocked = true;
|
mScreenOnWasBlocked = true;
|
||||||
@@ -783,25 +649,8 @@ final class DisplayPowerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int clampScreenBrightness(int value) {
|
private int clampScreenBrightness(int value) {
|
||||||
return clamp(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
|
return MathUtils.constrain(
|
||||||
}
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateScreenBrightness(int target, int rate) {
|
private void animateScreenBrightness(int target, int rate) {
|
||||||
@@ -902,270 +751,6 @@ final class DisplayPowerController {
|
|||||||
mPendingProximityDebounceTime = debounceTime;
|
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() {
|
private void sendOnStateChangedWithWakelock() {
|
||||||
mDisplaySuspendBlocker.acquire();
|
mDisplaySuspendBlocker.acquire();
|
||||||
mCallbackHandler.post(mOnStateChangedRunnable);
|
mCallbackHandler.post(mOnStateChangedRunnable);
|
||||||
@@ -1224,8 +809,6 @@ final class DisplayPowerController {
|
|||||||
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
|
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
|
||||||
pw.println(" mUseSoftwareAutoBrightnessConfig="
|
pw.println(" mUseSoftwareAutoBrightnessConfig="
|
||||||
+ mUseSoftwareAutoBrightnessConfig);
|
+ mUseSoftwareAutoBrightnessConfig);
|
||||||
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
|
|
||||||
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
|
|
||||||
|
|
||||||
mHandler.runWithScissors(new Runnable() {
|
mHandler.runWithScissors(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -1249,25 +832,7 @@ final class DisplayPowerController {
|
|||||||
pw.println(" mPendingProximityDebounceTime="
|
pw.println(" mPendingProximityDebounceTime="
|
||||||
+ TimeUtils.formatUptime(mPendingProximityDebounceTime));
|
+ TimeUtils.formatUptime(mPendingProximityDebounceTime));
|
||||||
pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
|
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(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
|
||||||
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
|
|
||||||
pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
|
|
||||||
|
|
||||||
if (mElectronBeamOnAnimator != null) {
|
if (mElectronBeamOnAnimator != null) {
|
||||||
pw.println(" mElectronBeamOnAnimator.isStarted()=" +
|
pw.println(" mElectronBeamOnAnimator.isStarted()=" +
|
||||||
@@ -1281,6 +846,11 @@ final class DisplayPowerController {
|
|||||||
if (mPowerState != null) {
|
if (mPowerState != null) {
|
||||||
mPowerState.dump(pw);
|
mPowerState.dump(pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mAutomaticBrightnessController != null) {
|
||||||
|
mAutomaticBrightnessController.dump(pw);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String proximityToString(int state) {
|
private static String proximityToString(int state) {
|
||||||
@@ -1296,13 +866,37 @@ final class DisplayPowerController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean wantScreenOn(int state) {
|
private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
|
||||||
switch (state) {
|
try {
|
||||||
case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
|
final int n = brightness.length;
|
||||||
case DisplayPowerRequest.SCREEN_STATE_DIM:
|
float[] x = new float[n];
|
||||||
return true;
|
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]);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
case MSG_PROXIMITY_SENSOR_DEBOUNCED:
|
||||||
debounceProximitySensor();
|
debounceProximitySensor();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MSG_LIGHT_SENSOR_DEBOUNCED:
|
|
||||||
debounceLightSensor();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1353,28 +943,4 @@ final class DisplayPowerController {
|
|||||||
// Not used.
|
// 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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,4 +114,13 @@ final class DisplayPowerRequest {
|
|||||||
+ ", useAutoBrightness=" + useAutoBrightness
|
+ ", useAutoBrightness=" + useAutoBrightness
|
||||||
+ ", blockScreenOn=" + blockScreenOn;
|
+ ", blockScreenOn=" + blockScreenOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean wantScreenOn(int state) {
|
||||||
|
switch(state) {
|
||||||
|
case SCREEN_STATE_DIM:
|
||||||
|
case SCREEN_STATE_BRIGHT:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user