Always use the ElectronBeam now, even when we are only animating the backlight so that we will have a black surface remaining on the screen after the screen turns off. When turning on the screen, keep the black surface showing until we unblock screen on then dismiss it as usual. This change eliminates the flashing of old display content when the screen is turned on. It also helps to conceal some of the latency of turning the screen on. We always turn the screen on immediately (even when screen on has nominally been blocked) and rely on the black surface to hide the screen contents until the last moment. Dismissing the black surface is practically instantaneous compared to turning the screen on. Bug: 7299370 Bug: 7139924 Change-Id: I57d13287acd05bd0a48811095bb02dc7bc7cbeb6
1279 lines
52 KiB
Java
1279 lines
52 KiB
Java
/*
|
|
* Copyright (C) 2012 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.LightsService;
|
|
import com.android.server.TwilightService;
|
|
import com.android.server.TwilightService.TwilightState;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.ObjectAnimator;
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.hardware.Sensor;
|
|
import android.hardware.SensorEvent;
|
|
import android.hardware.SensorEventListener;
|
|
import android.hardware.SensorManager;
|
|
import android.hardware.SystemSensorManager;
|
|
import android.hardware.display.DisplayManager;
|
|
import android.os.AsyncTask;
|
|
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.FloatMath;
|
|
import android.util.Slog;
|
|
import android.util.Spline;
|
|
import android.util.TimeUtils;
|
|
import android.view.Display;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* Controls the power state of the display.
|
|
*
|
|
* Handles the proximity sensor, light sensor, and animations between states
|
|
* including the screen off animation.
|
|
*
|
|
* This component acts independently of the rest of the power manager service.
|
|
* In particular, it does not share any state and it only communicates
|
|
* via asynchronous callbacks to inform the power manager that something has
|
|
* changed.
|
|
*
|
|
* Everything this class does internally is serialized on its handler although
|
|
* it may be accessed by other threads from the outside.
|
|
*
|
|
* Note that the power manager service guarantees that it will hold a suspend
|
|
* blocker as long as the display is not ready. So most of the work done here
|
|
* does not need to worry about holding a suspend blocker unless it happens
|
|
* independently of the display ready signal.
|
|
*
|
|
* 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 {
|
|
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
|
|
// actually turns on and starts showing new content after the call to set the
|
|
// 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;
|
|
|
|
// 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 ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 250;
|
|
private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 450;
|
|
|
|
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;
|
|
private static final int PROXIMITY_POSITIVE = 1;
|
|
|
|
// Proximity sensor debounce delay in milliseconds for positive or negative transitions.
|
|
private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
|
|
private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 500;
|
|
|
|
// Trigger proximity if distance is less than 5 cm.
|
|
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
|
|
|
|
// Light sensor event rate in microseconds.
|
|
private static final int LIGHT_SENSOR_RATE = 1000000;
|
|
|
|
// 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;
|
|
|
|
// Filter time constant in milliseconds for computing a moving
|
|
// average of light samples. Different constants are used
|
|
// to calculate the average light level when adapting to brighter or
|
|
// dimmer environments.
|
|
// This parameter only controls the filtering of light samples.
|
|
private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 600;
|
|
private static final long DIMMING_LIGHT_TIME_CONSTANT = 4000;
|
|
|
|
// 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 dimmer
|
|
// environments.
|
|
// This parameter controls how quickly brightness changes occur in response to
|
|
// an observed change in light level.
|
|
private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500;
|
|
private static final long DIMMING_LIGHT_DEBOUNCE = 10000;
|
|
|
|
private final Object mLock = new Object();
|
|
|
|
// Notifier for sending asynchronous notifications.
|
|
private final Notifier mNotifier;
|
|
|
|
// A suspend blocker.
|
|
private final SuspendBlocker mSuspendBlocker;
|
|
|
|
// Our handler.
|
|
private final DisplayControllerHandler mHandler;
|
|
|
|
// Asynchronous callbacks into the power manager service.
|
|
// Only invoked from the handler thread while no locks are held.
|
|
private final Callbacks mCallbacks;
|
|
private Handler mCallbackHandler;
|
|
|
|
// The lights service.
|
|
private final LightsService mLights;
|
|
|
|
// The twilight service.
|
|
private final TwilightService mTwilight;
|
|
|
|
// The display manager.
|
|
private final DisplayManager mDisplayManager;
|
|
|
|
// 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;
|
|
|
|
// The minimum allowed brightness.
|
|
private final int mScreenBrightnessRangeMinimum;
|
|
|
|
// The maximum allowed brightness.
|
|
private final int mScreenBrightnessRangeMaximum;
|
|
|
|
// 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 animate the backlight when turning the screen on or off, which
|
|
// tends to be efficient for LCD displays but not for OLED displays.
|
|
// False if we should play the electron beam animation instead, which is better for
|
|
// OLED displays.
|
|
private boolean mElectronBeamAnimatesBacklightConfig;
|
|
|
|
// The pending power request.
|
|
// Initially null until the first call to requestPowerState.
|
|
// Guarded by mLock.
|
|
private DisplayPowerRequest mPendingRequestLocked;
|
|
|
|
// True if a request has been made to wait for the proximity sensor to go negative.
|
|
// Guarded by mLock.
|
|
private boolean mPendingWaitForNegativeProximityLocked;
|
|
|
|
// True if the pending power request or wait for negative proximity flag
|
|
// has been changed since the last update occurred.
|
|
// Guarded by mLock.
|
|
private boolean mPendingRequestChangedLocked;
|
|
|
|
// Set to true when the important parts of the pending power request have been applied.
|
|
// The important parts are mainly the screen state. Brightness changes may occur
|
|
// concurrently.
|
|
// Guarded by mLock.
|
|
private boolean mDisplayReadyLocked;
|
|
|
|
// Set to true if a power state update is required.
|
|
// Guarded by mLock.
|
|
private boolean mPendingUpdatePowerStateLocked;
|
|
|
|
/* The following state must only be accessed by the handler thread. */
|
|
|
|
// The currently requested power state.
|
|
// The power controller will progressively update its internal state to match
|
|
// the requested power state. Initially null until the first update.
|
|
private DisplayPowerRequest mPowerRequest;
|
|
|
|
// The current power state.
|
|
// Must only be accessed on the handler thread.
|
|
private DisplayPowerState mPowerState;
|
|
|
|
// True if the device should wait for negative proximity sensor before
|
|
// waking up the screen. This is set to false as soon as a negative
|
|
// proximity sensor measurement is observed or when the device is forced to
|
|
// go to sleep by the user. While true, the screen remains off.
|
|
private boolean mWaitingForNegativeProximity;
|
|
|
|
// The actual proximity sensor threshold value.
|
|
private float mProximityThreshold;
|
|
|
|
// Set to true if the proximity sensor listener has been registered
|
|
// with the sensor manager.
|
|
private boolean mProximitySensorEnabled;
|
|
|
|
// The debounced proximity sensor state.
|
|
private int mProximity = PROXIMITY_UNKNOWN;
|
|
|
|
// The raw non-debounced proximity sensor state.
|
|
private int mPendingProximity = PROXIMITY_UNKNOWN;
|
|
private long mPendingProximityDebounceTime;
|
|
|
|
// True if the screen was turned off because of the proximity sensor.
|
|
// When the screen turns on again, we report user activity to the power manager.
|
|
private boolean mScreenOffBecauseOfProximity;
|
|
|
|
// True if the screen on is being blocked.
|
|
private boolean mScreenOnWasBlocked;
|
|
|
|
// 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 average light sensor value.
|
|
private float mLightMeasurement;
|
|
|
|
// True if the light sensor measurement is valid.
|
|
private boolean mLightMeasurementValid;
|
|
|
|
// The number of light sensor samples that have been collected since the
|
|
// last time a light sensor reading was accepted.
|
|
private int mRecentLightSamples;
|
|
|
|
// The moving average of recent light sensor values.
|
|
private float mRecentLightAverage;
|
|
|
|
// True if recent light samples are getting brighter than the previous
|
|
// stable light measurement.
|
|
private boolean mRecentLightBrightening;
|
|
|
|
// The time constant to use for filtering based on whether the
|
|
// light appears to be brightening or dimming.
|
|
private long mRecentLightTimeConstant;
|
|
|
|
// The most recent light sample.
|
|
private float mLastLightSample;
|
|
|
|
// The time of the most light recent sample.
|
|
private long mLastLightSampleTime;
|
|
|
|
// The time when we accumulated the first recent light sample into mRecentLightSamples.
|
|
private long mFirstRecentLightSampleTime;
|
|
|
|
// The upcoming debounce light sensor time.
|
|
// This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1.
|
|
private long mPendingLightSensorDebounceTime;
|
|
|
|
// 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;
|
|
|
|
// 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.
|
|
*/
|
|
public DisplayPowerController(Looper looper, Context context, Notifier notifier,
|
|
LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
|
|
Callbacks callbacks, Handler callbackHandler) {
|
|
mHandler = new DisplayControllerHandler(looper);
|
|
mNotifier = notifier;
|
|
mSuspendBlocker = suspendBlocker;
|
|
mCallbacks = callbacks;
|
|
mCallbackHandler = callbackHandler;
|
|
|
|
mLights = lights;
|
|
mTwilight = twilight;
|
|
mSensorManager = new SystemSensorManager(mHandler.getLooper());
|
|
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
|
|
|
|
final Resources resources = context.getResources();
|
|
|
|
mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
|
|
com.android.internal.R.integer.config_screenBrightnessDim));
|
|
|
|
int screenBrightnessMinimum = Math.min(resources.getInteger(
|
|
com.android.internal.R.integer.config_screenBrightnessSettingMinimum),
|
|
mScreenBrightnessDimConfig);
|
|
|
|
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
|
|
com.android.internal.R.bool.config_automatic_brightness_available);
|
|
if (mUseSoftwareAutoBrightnessConfig) {
|
|
int[] lux = resources.getIntArray(
|
|
com.android.internal.R.array.config_autoBrightnessLevels);
|
|
int[] screenBrightness = resources.getIntArray(
|
|
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
|
|
|
|
mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
|
|
if (mScreenAutoBrightnessSpline == null) {
|
|
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
|
|
+ "(size " + screenBrightness.length + ") "
|
|
+ "must be monotic and have exactly one more entry than "
|
|
+ "config_autoBrightnessLevels (size " + lux.length + ") "
|
|
+ "which must be strictly increasing. "
|
|
+ "Auto-brightness will be disabled.");
|
|
mUseSoftwareAutoBrightnessConfig = false;
|
|
} else {
|
|
if (screenBrightness[0] < screenBrightnessMinimum) {
|
|
screenBrightnessMinimum = screenBrightness[0];
|
|
}
|
|
}
|
|
|
|
mLightSensorWarmUpTimeConfig = resources.getInteger(
|
|
com.android.internal.R.integer.config_lightSensorWarmupTime);
|
|
}
|
|
|
|
mScreenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightnessMinimum);
|
|
mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
|
|
|
|
mElectronBeamAnimatesBacklightConfig = resources.getBoolean(
|
|
com.android.internal.R.bool.config_animateScreenLights);
|
|
|
|
if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
|
|
mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
|
|
if (mProximitySensor != null) {
|
|
mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
|
|
TYPICAL_PROXIMITY_THRESHOLD);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the proximity sensor screen-off function is available.
|
|
*/
|
|
public boolean isProximitySensorAvailable() {
|
|
return mProximitySensor != null;
|
|
}
|
|
|
|
/**
|
|
* Requests a new power state.
|
|
* The controller makes a copy of the provided object and then
|
|
* begins adjusting the power state to match what was requested.
|
|
*
|
|
* @param request The requested power state.
|
|
* @param waitForNegativeProximity If true, issues a request to wait for
|
|
* negative proximity before turning the screen back on, assuming the screen
|
|
* was turned off by the proximity sensor.
|
|
* @return True if display is ready, false if there are important changes that must
|
|
* be made asynchronously (such as turning the screen on), in which case the caller
|
|
* should grab a wake lock, watch for {@link Callbacks#onStateChanged()} then try
|
|
* the request again later until the state converges.
|
|
*/
|
|
public boolean requestPowerState(DisplayPowerRequest request,
|
|
boolean waitForNegativeProximity) {
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "requestPowerState: "
|
|
+ request + ", waitForNegativeProximity=" + waitForNegativeProximity);
|
|
}
|
|
|
|
synchronized (mLock) {
|
|
boolean changed = false;
|
|
|
|
if (waitForNegativeProximity
|
|
&& !mPendingWaitForNegativeProximityLocked) {
|
|
mPendingWaitForNegativeProximityLocked = true;
|
|
changed = true;
|
|
}
|
|
|
|
if (mPendingRequestLocked == null) {
|
|
mPendingRequestLocked = new DisplayPowerRequest(request);
|
|
changed = true;
|
|
} else if (!mPendingRequestLocked.equals(request)) {
|
|
mPendingRequestLocked.copyFrom(request);
|
|
changed = true;
|
|
}
|
|
|
|
if (changed) {
|
|
mDisplayReadyLocked = false;
|
|
}
|
|
|
|
if (changed && !mPendingRequestChangedLocked) {
|
|
mPendingRequestChangedLocked = true;
|
|
sendUpdatePowerStateLocked();
|
|
}
|
|
|
|
return mDisplayReadyLocked;
|
|
}
|
|
}
|
|
|
|
private void sendUpdatePowerState() {
|
|
synchronized (mLock) {
|
|
sendUpdatePowerStateLocked();
|
|
}
|
|
}
|
|
|
|
private void sendUpdatePowerStateLocked() {
|
|
if (!mPendingUpdatePowerStateLocked) {
|
|
mPendingUpdatePowerStateLocked = true;
|
|
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
|
|
msg.setAsynchronous(true);
|
|
mHandler.sendMessage(msg);
|
|
}
|
|
}
|
|
|
|
private void initialize() {
|
|
final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
|
|
Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
|
|
mPowerState = new DisplayPowerState(
|
|
new ElectronBeam(display),
|
|
new PhotonicModulator(executor,
|
|
mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
|
|
mSuspendBlocker));
|
|
|
|
mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
|
|
mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
|
|
mElectronBeamOnAnimator.setDuration(ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS);
|
|
mElectronBeamOnAnimator.addListener(mAnimatorListener);
|
|
|
|
mElectronBeamOffAnimator = ObjectAnimator.ofFloat(
|
|
mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 1.0f, 0.0f);
|
|
mElectronBeamOffAnimator.setDuration(ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS);
|
|
mElectronBeamOffAnimator.addListener(mAnimatorListener);
|
|
|
|
mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
|
|
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
|
|
}
|
|
|
|
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
|
|
@Override
|
|
public void onAnimationStart(Animator animation) {
|
|
}
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
sendUpdatePowerState();
|
|
}
|
|
@Override
|
|
public void onAnimationRepeat(Animator animation) {
|
|
}
|
|
@Override
|
|
public void onAnimationCancel(Animator animation) {
|
|
}
|
|
};
|
|
|
|
private void updatePowerState() {
|
|
// Update the power state request.
|
|
final boolean mustNotify;
|
|
boolean mustInitialize = false;
|
|
boolean updateAutoBrightness = mTwilightChanged;
|
|
mTwilightChanged = false;
|
|
|
|
synchronized (mLock) {
|
|
mPendingUpdatePowerStateLocked = false;
|
|
if (mPendingRequestLocked == null) {
|
|
return; // wait until first actual power request
|
|
}
|
|
|
|
if (mPowerRequest == null) {
|
|
mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
|
|
mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
|
|
mPendingWaitForNegativeProximityLocked = false;
|
|
mPendingRequestChangedLocked = false;
|
|
mustInitialize = true;
|
|
} else if (mPendingRequestChangedLocked) {
|
|
if (mPowerRequest.screenAutoBrightnessAdjustment
|
|
!= mPendingRequestLocked.screenAutoBrightnessAdjustment) {
|
|
updateAutoBrightness = true;
|
|
}
|
|
mPowerRequest.copyFrom(mPendingRequestLocked);
|
|
mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
|
|
mPendingWaitForNegativeProximityLocked = false;
|
|
mPendingRequestChangedLocked = false;
|
|
mDisplayReadyLocked = false;
|
|
}
|
|
|
|
mustNotify = !mDisplayReadyLocked;
|
|
}
|
|
|
|
// Initialize things the first time the power state is changed.
|
|
if (mustInitialize) {
|
|
initialize();
|
|
}
|
|
|
|
// Apply the proximity sensor.
|
|
if (mProximitySensor != null) {
|
|
if (mPowerRequest.useProximitySensor
|
|
&& mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
|
|
setProximitySensorEnabled(true);
|
|
if (!mScreenOffBecauseOfProximity
|
|
&& mProximity == PROXIMITY_POSITIVE) {
|
|
mScreenOffBecauseOfProximity = true;
|
|
sendOnProximityPositive();
|
|
setScreenOn(false);
|
|
}
|
|
} else if (mWaitingForNegativeProximity
|
|
&& mScreenOffBecauseOfProximity
|
|
&& mProximity == PROXIMITY_POSITIVE
|
|
&& mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
|
|
setProximitySensorEnabled(true);
|
|
} else {
|
|
setProximitySensorEnabled(false);
|
|
mWaitingForNegativeProximity = false;
|
|
}
|
|
if (mScreenOffBecauseOfProximity
|
|
&& mProximity != PROXIMITY_POSITIVE) {
|
|
mScreenOffBecauseOfProximity = false;
|
|
sendOnProximityNegative();
|
|
}
|
|
} else {
|
|
mWaitingForNegativeProximity = false;
|
|
}
|
|
|
|
// Turn on the light sensor if needed.
|
|
if (mLightSensor != null) {
|
|
setLightSensorEnabled(mPowerRequest.useAutoBrightness
|
|
&& wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
|
|
}
|
|
|
|
// Set the screen brightness.
|
|
if (wantScreenOn(mPowerRequest.screenState)) {
|
|
int target;
|
|
boolean slow;
|
|
if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
|
|
// Use current auto-brightness value.
|
|
target = mScreenAutoBrightness;
|
|
slow = mUsingScreenAutoBrightness;
|
|
mUsingScreenAutoBrightness = true;
|
|
} else {
|
|
// Light sensor is disabled or not ready yet.
|
|
// Use the current brightness setting from the request, which is expected
|
|
// provide a nominal default value for the case where auto-brightness
|
|
// is not ready yet.
|
|
target = mPowerRequest.screenBrightness;
|
|
slow = false;
|
|
mUsingScreenAutoBrightness = false;
|
|
}
|
|
if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
|
|
// Screen is dimmed. Sets an upper bound on everything else.
|
|
target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION,
|
|
mScreenBrightnessDimConfig);
|
|
slow = false;
|
|
}
|
|
animateScreenBrightness(clampScreenBrightness(target),
|
|
slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
|
|
} else {
|
|
// Screen is off. Don't bother changing the brightness.
|
|
mUsingScreenAutoBrightness = false;
|
|
}
|
|
|
|
// Animate the screen on or off.
|
|
if (!mScreenOffBecauseOfProximity) {
|
|
if (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
|
|
// on animation immediately then the results are pretty ugly.
|
|
if (!mElectronBeamOffAnimator.isStarted()) {
|
|
// Turn the screen on. The contents of the screen may not yet
|
|
// be visible if the electron beam has not been dismissed because
|
|
// its last frame of animation is solid black.
|
|
setScreenOn(true);
|
|
|
|
if (mPowerRequest.blockScreenOn
|
|
&& mPowerState.getElectronBeamLevel() == 0.0f) {
|
|
blockScreenOn();
|
|
} else {
|
|
unblockScreenOn();
|
|
if (USE_ELECTRON_BEAM_ON_ANIMATION) {
|
|
if (!mElectronBeamOnAnimator.isStarted()) {
|
|
if (mPowerState.getElectronBeamLevel() == 1.0f) {
|
|
mPowerState.dismissElectronBeam();
|
|
} else if (mPowerState.prepareElectronBeam(
|
|
mElectronBeamAnimatesBacklightConfig ?
|
|
ElectronBeam.MODE_BLANK :
|
|
ElectronBeam.MODE_WARM_UP)) {
|
|
mElectronBeamOnAnimator.start();
|
|
} else {
|
|
mElectronBeamOnAnimator.end();
|
|
}
|
|
}
|
|
} else {
|
|
mPowerState.setElectronBeamLevel(1.0f);
|
|
mPowerState.dismissElectronBeam();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Want screen off.
|
|
// Wait for previous on animation to complete beforehand.
|
|
if (!mElectronBeamOnAnimator.isStarted()) {
|
|
if (!mElectronBeamOffAnimator.isStarted()) {
|
|
if (mPowerState.getElectronBeamLevel() == 0.0f) {
|
|
setScreenOn(false);
|
|
} else if (mPowerState.prepareElectronBeam(
|
|
mElectronBeamAnimatesBacklightConfig ?
|
|
ElectronBeam.MODE_BLANK :
|
|
ElectronBeam.MODE_COOL_DOWN)
|
|
&& mPowerState.isScreenOn()) {
|
|
mElectronBeamOffAnimator.start();
|
|
} else {
|
|
mElectronBeamOffAnimator.end();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Report whether the display is ready for use.
|
|
// We mostly care about the screen state here, ignoring brightness changes
|
|
// which will be handled asynchronously.
|
|
if (mustNotify
|
|
&& !mScreenOnWasBlocked
|
|
&& !mElectronBeamOnAnimator.isStarted()
|
|
&& !mElectronBeamOffAnimator.isStarted()
|
|
&& mPowerState.waitUntilClean(mCleanListener)) {
|
|
synchronized (mLock) {
|
|
if (!mPendingRequestChangedLocked) {
|
|
mDisplayReadyLocked = true;
|
|
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "Display ready!");
|
|
}
|
|
}
|
|
}
|
|
sendOnStateChanged();
|
|
}
|
|
}
|
|
|
|
private void blockScreenOn() {
|
|
if (!mScreenOnWasBlocked) {
|
|
mScreenOnWasBlocked = true;
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "Blocked screen on.");
|
|
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void unblockScreenOn() {
|
|
if (mScreenOnWasBlocked) {
|
|
mScreenOnWasBlocked = false;
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "Unblocked screen on after " +
|
|
(SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setScreenOn(boolean on) {
|
|
if (!mPowerState.isScreenOn() == on) {
|
|
mPowerState.setScreenOn(on);
|
|
if (on) {
|
|
mNotifier.onScreenOn();
|
|
} else {
|
|
mNotifier.onScreenOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
private void animateScreenBrightness(int target, int rate) {
|
|
if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
|
|
mNotifier.onScreenBrightness(target);
|
|
}
|
|
}
|
|
|
|
private final Runnable mCleanListener = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
sendUpdatePowerState();
|
|
}
|
|
};
|
|
|
|
private void setProximitySensorEnabled(boolean enable) {
|
|
if (enable) {
|
|
if (!mProximitySensorEnabled) {
|
|
mProximitySensorEnabled = true;
|
|
mPendingProximity = PROXIMITY_UNKNOWN;
|
|
mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
|
|
SensorManager.SENSOR_DELAY_NORMAL, mHandler);
|
|
}
|
|
} else {
|
|
if (mProximitySensorEnabled) {
|
|
mProximitySensorEnabled = false;
|
|
mProximity = PROXIMITY_UNKNOWN;
|
|
mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
|
|
mSensorManager.unregisterListener(mProximitySensorListener);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleProximitySensorEvent(long time, boolean positive) {
|
|
if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
|
|
return; // no change
|
|
}
|
|
if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
|
|
return; // no change
|
|
}
|
|
|
|
// Only accept a proximity sensor reading if it remains
|
|
// stable for the entire debounce delay.
|
|
mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
|
|
if (positive) {
|
|
mPendingProximity = PROXIMITY_POSITIVE;
|
|
mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY;
|
|
} else {
|
|
mPendingProximity = PROXIMITY_NEGATIVE;
|
|
mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY;
|
|
}
|
|
debounceProximitySensor();
|
|
}
|
|
|
|
private void debounceProximitySensor() {
|
|
if (mPendingProximity != PROXIMITY_UNKNOWN) {
|
|
final long now = SystemClock.uptimeMillis();
|
|
if (mPendingProximityDebounceTime <= now) {
|
|
mProximity = mPendingProximity;
|
|
sendUpdatePowerState();
|
|
} else {
|
|
Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
|
|
msg.setAsynchronous(true);
|
|
mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, mHandler);
|
|
}
|
|
} else {
|
|
if (mLightSensorEnabled) {
|
|
mLightSensorEnabled = false;
|
|
mLightMeasurementValid = false;
|
|
mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
|
|
mSensorManager.unregisterListener(mLightSensorListener);
|
|
}
|
|
}
|
|
if (updateAutoBrightness) {
|
|
updateAutoBrightness(false);
|
|
}
|
|
}
|
|
|
|
private void handleLightSensorEvent(long time, float lux) {
|
|
// Take the first few readings during the warm-up period and apply them
|
|
// immediately without debouncing.
|
|
if (!mLightMeasurementValid
|
|
|| (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
|
|
mLightMeasurement = lux;
|
|
mLightMeasurementValid = true;
|
|
mRecentLightSamples = 0;
|
|
updateAutoBrightness(true);
|
|
}
|
|
|
|
// Update our moving average.
|
|
if (lux != mLightMeasurement && (mRecentLightSamples == 0
|
|
|| (lux < mLightMeasurement && mRecentLightBrightening)
|
|
|| (lux > mLightMeasurement && !mRecentLightBrightening))) {
|
|
// If the newest light sample doesn't seem to be going in the
|
|
// same general direction as recent samples, then start over.
|
|
setRecentLight(time, lux, lux > mLightMeasurement);
|
|
} else if (mRecentLightSamples >= 1) {
|
|
// Add the newest light sample to the moving average.
|
|
accumulateRecentLight(time, lux);
|
|
}
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "handleLightSensorEvent: lux=" + lux
|
|
+ ", mLightMeasurementValid=" + mLightMeasurementValid
|
|
+ ", mLightMeasurement=" + mLightMeasurement
|
|
+ ", mRecentLightSamples=" + mRecentLightSamples
|
|
+ ", mRecentLightAverage=" + mRecentLightAverage
|
|
+ ", mRecentLightBrightening=" + mRecentLightBrightening
|
|
+ ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
|
|
+ ", mFirstRecentLightSampleTime="
|
|
+ TimeUtils.formatUptime(mFirstRecentLightSampleTime)
|
|
+ ", mPendingLightSensorDebounceTime="
|
|
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
|
|
}
|
|
|
|
// Debounce.
|
|
mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
|
|
debounceLightSensor();
|
|
}
|
|
|
|
private void setRecentLight(long time, float lux, boolean brightening) {
|
|
mRecentLightBrightening = brightening;
|
|
mRecentLightTimeConstant = brightening ?
|
|
BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT;
|
|
mRecentLightSamples = 1;
|
|
mRecentLightAverage = lux;
|
|
mLastLightSample = lux;
|
|
mLastLightSampleTime = time;
|
|
mFirstRecentLightSampleTime = time;
|
|
mPendingLightSensorDebounceTime = time + (brightening ?
|
|
BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE);
|
|
}
|
|
|
|
private void accumulateRecentLight(long time, float lux) {
|
|
final long timeDelta = time - mLastLightSampleTime;
|
|
mRecentLightSamples += 1;
|
|
mRecentLightAverage += (lux - mRecentLightAverage) *
|
|
timeDelta / (mRecentLightTimeConstant + timeDelta);
|
|
mLastLightSample = lux;
|
|
mLastLightSampleTime = time;
|
|
}
|
|
|
|
private void debounceLightSensor() {
|
|
if (mLightMeasurementValid && mRecentLightSamples >= 1) {
|
|
final long now = SystemClock.uptimeMillis();
|
|
if (mPendingLightSensorDebounceTime <= now) {
|
|
accumulateRecentLight(now, mLastLightSample);
|
|
mLightMeasurement = mRecentLightAverage;
|
|
|
|
if (DEBUG) {
|
|
Slog.d(TAG, "debounceLightSensor: Accepted new measurement "
|
|
+ mLightMeasurement + " after "
|
|
+ (now - mFirstRecentLightSampleTime) + " ms based on "
|
|
+ mRecentLightSamples + " recent samples.");
|
|
}
|
|
|
|
updateAutoBrightness(true);
|
|
|
|
// Now that we have debounced the light sensor data, we have the
|
|
// option of either leaving the sensor in a debounced state or
|
|
// restarting the debounce cycle by setting mRecentLightSamples to 0.
|
|
//
|
|
// If we leave the sensor debounced, then new average light measurements
|
|
// may be accepted immediately as long as they are trending in the same
|
|
// direction as they were before. If the measurements start
|
|
// jittering or trending in the opposite direction then the debounce
|
|
// cycle will automatically be restarted. The benefit is that the
|
|
// auto-brightness control can be more responsive to changes over a
|
|
// broad range.
|
|
//
|
|
// For now, we choose to be more responsive and leave the following line
|
|
// commented out.
|
|
//
|
|
// mRecentLightSamples = 0;
|
|
} else {
|
|
Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED);
|
|
msg.setAsynchronous(true);
|
|
mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateAutoBrightness(boolean sendUpdate) {
|
|
if (!mLightMeasurementValid) {
|
|
return;
|
|
}
|
|
|
|
float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement);
|
|
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(
|
|
(int)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 sendOnStateChanged() {
|
|
mCallbackHandler.post(mOnStateChangedRunnable);
|
|
}
|
|
|
|
private final Runnable mOnStateChangedRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mCallbacks.onStateChanged();
|
|
}
|
|
};
|
|
|
|
private void sendOnProximityPositive() {
|
|
mCallbackHandler.post(mOnProximityPositiveRunnable);
|
|
}
|
|
|
|
private final Runnable mOnProximityPositiveRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mCallbacks.onProximityPositive();
|
|
}
|
|
};
|
|
|
|
private void sendOnProximityNegative() {
|
|
mCallbackHandler.post(mOnProximityNegativeRunnable);
|
|
}
|
|
|
|
private final Runnable mOnProximityNegativeRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mCallbacks.onProximityNegative();
|
|
}
|
|
};
|
|
|
|
public void dump(final PrintWriter pw) {
|
|
synchronized (mLock) {
|
|
pw.println();
|
|
pw.println("Display Controller Locked State:");
|
|
pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked);
|
|
pw.println(" mPendingRequestLocked=" + mPendingRequestLocked);
|
|
pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
|
|
pw.println(" mPendingWaitForNegativeProximityLocked="
|
|
+ mPendingWaitForNegativeProximityLocked);
|
|
pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
|
|
}
|
|
|
|
pw.println();
|
|
pw.println("Display Controller Configuration:");
|
|
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
|
|
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
|
|
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
|
|
pw.println(" mUseSoftwareAutoBrightnessConfig="
|
|
+ mUseSoftwareAutoBrightnessConfig);
|
|
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
|
|
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
|
|
|
|
mHandler.runWithScissors(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
dumpLocal(pw);
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
private void dumpLocal(PrintWriter pw) {
|
|
pw.println();
|
|
pw.println("Display Controller Thread State:");
|
|
pw.println(" mPowerRequest=" + mPowerRequest);
|
|
pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
|
|
|
|
pw.println(" mProximitySensor=" + mProximitySensor);
|
|
pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
|
|
pw.println(" mProximityThreshold=" + mProximityThreshold);
|
|
pw.println(" mProximity=" + proximityToString(mProximity));
|
|
pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
|
|
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(" mLightMeasurement=" + mLightMeasurement);
|
|
pw.println(" mLightMeasurementValid=" + mLightMeasurementValid);
|
|
pw.println(" mLastLightSample=" + mLastLightSample);
|
|
pw.println(" mLastLightSampleTime="
|
|
+ TimeUtils.formatUptime(mLastLightSampleTime));
|
|
pw.println(" mRecentLightSamples=" + mRecentLightSamples);
|
|
pw.println(" mRecentLightAverage=" + mRecentLightAverage);
|
|
pw.println(" mRecentLightBrightening=" + mRecentLightBrightening);
|
|
pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant);
|
|
pw.println(" mFirstRecentLightSampleTime="
|
|
+ TimeUtils.formatUptime(mFirstRecentLightSampleTime));
|
|
pw.println(" mPendingLightSensorDebounceTime="
|
|
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
|
|
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()=" +
|
|
mElectronBeamOnAnimator.isStarted());
|
|
}
|
|
if (mElectronBeamOffAnimator != null) {
|
|
pw.println(" mElectronBeamOffAnimator.isStarted()=" +
|
|
mElectronBeamOffAnimator.isStarted());
|
|
}
|
|
|
|
if (mPowerState != null) {
|
|
mPowerState.dump(pw);
|
|
}
|
|
}
|
|
|
|
private static String proximityToString(int state) {
|
|
switch (state) {
|
|
case PROXIMITY_UNKNOWN:
|
|
return "Unknown";
|
|
case PROXIMITY_NEGATIVE:
|
|
return "Negative";
|
|
case PROXIMITY_POSITIVE:
|
|
return "Positive";
|
|
default:
|
|
return Integer.toString(state);
|
|
}
|
|
}
|
|
|
|
private static boolean wantScreenOn(int state) {
|
|
switch (state) {
|
|
case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
|
|
case DisplayPowerRequest.SCREEN_STATE_DIM:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Asynchronous callbacks from the power controller to the power manager service.
|
|
*/
|
|
public interface Callbacks {
|
|
void onStateChanged();
|
|
void onProximityPositive();
|
|
void onProximityNegative();
|
|
}
|
|
|
|
private final class DisplayControllerHandler extends Handler {
|
|
public DisplayControllerHandler(Looper looper) {
|
|
super(looper, null, true /*async*/);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case MSG_UPDATE_POWER_STATE:
|
|
updatePowerState();
|
|
break;
|
|
|
|
case MSG_PROXIMITY_SENSOR_DEBOUNCED:
|
|
debounceProximitySensor();
|
|
break;
|
|
|
|
case MSG_LIGHT_SENSOR_DEBOUNCED:
|
|
debounceLightSensor();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
|
|
@Override
|
|
public void onSensorChanged(SensorEvent event) {
|
|
if (mProximitySensorEnabled) {
|
|
final long time = SystemClock.uptimeMillis();
|
|
final float distance = event.values[0];
|
|
boolean positive = distance >= 0.0f && distance < mProximityThreshold;
|
|
handleProximitySensorEvent(time, positive);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
|
// 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 TwilightService.TwilightListener mTwilightListener =
|
|
new TwilightService.TwilightListener() {
|
|
@Override
|
|
public void onTwilightStateChanged() {
|
|
mTwilightChanged = true;
|
|
updatePowerState();
|
|
}
|
|
};
|
|
}
|