diff options
Diffstat (limited to 'services')
18 files changed, 4371 insertions, 2973 deletions
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index ea19d6e..f966a33 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -1626,7 +1626,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mLastMaximumTimeToLock = timeMs; try { - getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs); } catch (RemoteException e) { Slog.w(TAG, "Failure talking with power manager", e); } @@ -1667,8 +1667,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { // Power off the display - mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(), - WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN); + getIPowerManager().goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN); // Ensure the device is locked getWindowManager().lockNow(); } catch (RemoteException e) { diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 6f050d3..8bdd7be 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -33,6 +33,7 @@ import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.PowerManager; import android.os.SystemClock; import android.os.UEventObserver; import android.provider.Settings; @@ -64,11 +65,8 @@ class DockObserver extends UEventObserver { private final Context mContext; - private PowerManagerService mPowerManager; - - public DockObserver(Context context, PowerManagerService pm) { + public DockObserver(Context context) { mContext = context; - mPowerManager = pm; init(); // set initial status startObserving(DOCK_UEVENT_MATCH); @@ -94,8 +92,9 @@ class DockObserver extends UEventObserver { && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), - false, true); + PowerManager pm = + (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + pm.wakeUp(SystemClock.uptimeMillis()); } update(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e8350c1..c471dd2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -50,6 +50,7 @@ import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.widget.LockSettingsService; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.am.ActivityManagerService; +import com.android.server.am.BatteryStatsService; import com.android.server.display.DisplayManagerService; import com.android.server.input.InputManagerService; import com.android.server.net.NetworkPolicyManagerService; @@ -230,7 +231,8 @@ class ServerThread extends Thread { // only initialize the power service after we have started the // lights service, content providers and the battery service. - power.init(context, lights, ActivityManagerService.self(), battery, display); + power.init(context, lights, ActivityManagerService.self(), battery, + BatteryStatsService.getService(), display); Slog.i(TAG, "Alarm Manager"); alarm = new AlarmManagerService(context); @@ -553,7 +555,7 @@ class ServerThread extends Thread { try { Slog.i(TAG, "Dock Observer"); // Listen for dock station changes - dock = new DockObserver(context, power); + dock = new DockObserver(context); } catch (Throwable e) { reportWtf("starting DockObserver", e); } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index c5b4a07..9edfad6 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -349,7 +349,7 @@ public class Watchdog extends Thread { } if (mMinScreenOff >= 0 && (mPower == null || - mPower.timeSinceScreenOn() < mMinScreenOff)) { + mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) { return "screen"; } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java new file mode 100644 index 0000000..50d3f81 --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -0,0 +1,992 @@ +/* + * 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 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.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.util.Slog; +import android.util.TimeUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +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; + + private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 300; + private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 600; + + 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. + private static final int PROXIMITY_SENSOR_DEBOUNCE_DELAY = 250; + + // 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 = 50; + + // Filter time constant in milliseconds for computing a moving + // average of light samples. Different constants are used + // to adapt to brighter or dimmer environments. + private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 2500; // 2.5 sec + private static final long DIMMING_LIGHT_TIME_CONSTANT = 10000; // 10 sec + + 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 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; + + // Auto-brightness. + private boolean mUseSoftwareAutoBrightnessConfig; + private int[] mAutoBrightnessLevelsConfig; + private int[] mAutoBrightnessLcdBacklightValuesConfig; + + // 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; + + // 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; + + // 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 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; + + // 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; + + /** + * Creates the display power controller. + */ + public DisplayPowerController(Looper looper, Context context, Notifier notifier, + LightsService lights, SuspendBlocker suspendBlocker, + Callbacks callbacks, Handler callbackHandler) { + mHandler = new DisplayControllerHandler(looper); + mNotifier = notifier; + mSuspendBlocker = suspendBlocker; + mCallbacks = callbacks; + mCallbackHandler = callbackHandler; + + mLights = lights; + mSensorManager = new SystemSensorManager(mHandler.getLooper()); + + final Resources resources = context.getResources(); + mScreenBrightnessDimConfig = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDim); + mUseSoftwareAutoBrightnessConfig = resources.getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available); + if (mUseSoftwareAutoBrightnessConfig) { + mAutoBrightnessLevelsConfig = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLevels); + mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); + if (mAutoBrightnessLcdBacklightValuesConfig.length + != mAutoBrightnessLevelsConfig.length + 1) { + Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues " + + "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") " + + "should have exactly one more entry than " + + "config_autoBrightnessLevels (size " + + mAutoBrightnessLevelsConfig.length + "). " + + "Auto-brightness will be disabled."); + mUseSoftwareAutoBrightnessConfig = false; + } + + mLightSensorWarmUpTimeConfig = resources.getInteger( + com.android.internal.R.integer.config_lightSensorWarmupTime); + } + + 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); + } + } + + /** + * 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; + mPowerState = new DisplayPowerState(new ElectronBeam(), + 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; + synchronized (mLock) { + mPendingUpdatePowerStateLocked = false; + if (mPendingRequestLocked == null) { + return; // wait until first actual power request + } + + if (mPowerRequest == null) { + mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); + mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked; + mPendingRequestChangedLocked = false; + mustInitialize = true; + } else if (mPendingRequestChangedLocked) { + mPowerRequest.copyFrom(mPendingRequestLocked); + mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; + mPendingRequestChangedLocked = false; + mDisplayReadyLocked = false; + } + + mustNotify = !mDisplayReadyLocked; + } + + // Initialize things the first time the power state is changed. + if (mustInitialize) { + initialize(); + } + + // Clear a request to wait for negative proximity if needed. + if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_OFF + || mProximity == PROXIMITY_NEGATIVE + || mProximitySensor == null) { + mWaitingForNegativeProximity = false; + } + + // Turn on the proximity sensor if needed. + if (mProximitySensor != null) { + setProximitySensorEnabled(mPowerRequest.useProximitySensor + || mWaitingForNegativeProximity); + if (mProximitySensorEnabled && mProximity == PROXIMITY_POSITIVE) { + mScreenOffBecauseOfProximity = true; + setScreenOn(false); + } else if (mScreenOffBecauseOfProximity) { + mScreenOffBecauseOfProximity = false; + sendOnProximityNegative(); + } + } + + // Turn on the light sensor if needed. + if (mLightSensor != null) { + setLightSensorEnabled(mPowerRequest.useAutoBrightness + && wantScreenOn(mPowerRequest.screenState)); + } + + // Set the screen brightness. + if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) { + // Screen is dimmed. Overrides everything else. + animateScreenBrightness(mScreenBrightnessDimConfig, BRIGHTNESS_RAMP_RATE_FAST); + mUsingScreenAutoBrightness = false; + } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) { + if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) { + // Use current auto-brightness value. + animateScreenBrightness( + Math.max(mScreenAutoBrightness, mScreenBrightnessDimConfig), + mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW : + BRIGHTNESS_RAMP_RATE_FAST); + 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. + animateScreenBrightness( + Math.max(mPowerRequest.screenBrightness, mScreenBrightnessDimConfig), + BRIGHTNESS_RAMP_RATE_FAST); + mUsingScreenAutoBrightness = false; + } + } 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()) { + setScreenOn(true); + if (!mElectronBeamOnAnimator.isStarted()) { + if (mPowerState.getElectronBeamLevel() == 1.0f) { + mPowerState.dismissElectronBeam(); + } else if (mPowerState.prepareElectronBeam(true)) { + mElectronBeamOnAnimator.start(); + } else { + mElectronBeamOnAnimator.end(); + } + } + } + } 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(false) + && 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 + && !mElectronBeamOnAnimator.isStarted() + && !mElectronBeamOffAnimator.isStarted() + && mPowerState.waitUntilClean(mCleanListener)) { + synchronized (mLock) { + if (!mPendingRequestChangedLocked) { + mDisplayReadyLocked = true; + } + } + sendOnStateChanged(); + } + } + + private void setScreenOn(boolean on) { + if (!mPowerState.isScreenOn() == on) { + mPowerState.setScreenOn(on); + if (on) { + mNotifier.onScreenOn(); + } else { + mNotifier.onScreenOff(); + } + } + } + + 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); + mPendingProximity = positive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE; + mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_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) { + if (enable) { + if (!mLightSensorEnabled) { + mLightSensorEnabled = true; + mLightSensorEnableTime = SystemClock.uptimeMillis(); + mSensorManager.registerListener(mLightSensorListener, mLightSensor, + LIGHT_SENSOR_RATE, mHandler); + } + } else { + if (mLightSensorEnabled) { + mLightSensorEnabled = false; + mLightMeasurementValid = false; + updateAutoBrightness(false); + mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); + mSensorManager.unregisterListener(mLightSensorListener); + } + } + } + + 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); + mPendingLightSensorDebounceTime = time + mRecentLightTimeConstant; + } 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 + + ", 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; + } + + 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 - mPendingLightSensorDebounceTime + + mRecentLightTimeConstant) + " 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; + } + + final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement, + mAutoBrightnessLevelsConfig, + mAutoBrightnessLcdBacklightValuesConfig); + if (mScreenAutoBrightness != newScreenAutoBrightness) { + if (DEBUG) { + Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" + + mScreenAutoBrightness); + } + + mScreenAutoBrightness = newScreenAutoBrightness; + if (sendUpdate) { + sendUpdatePowerState(); + } + } + } + + /** + * Maps a light sensor measurement in lux to a brightness value given + * a table of lux breakpoint values and a table of brightnesses that + * is one element larger. + */ + private static int mapLuxToBrightness(float lux, + int[] fromLux, int[] toBrightness) { + // TODO implement interpolation and possibly range expansion + int level = 0; + final int count = fromLux.length; + while (level < count && lux >= fromLux[level]) { + level += 1; + } + return toBrightness[level]; + } + + private void sendOnStateChanged() { + mCallbackHandler.post(mOnStateChangedRunnable); + } + + private final Runnable mOnStateChangedRunnable = new Runnable() { + @Override + public void run() { + mCallbacks.onStateChanged(); + } + }; + + private void sendOnProximityNegative() { + mCallbackHandler.post(mOnProximityNegativeRunnable); + } + + private final Runnable mOnProximityNegativeRunnable = new Runnable() { + @Override + public void run() { + mCallbacks.onProximityNegative(); + } + }; + + public void dump(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(" mUseSoftwareAutoBrightnessConfig=" + + mUseSoftwareAutoBrightnessConfig); + pw.println(" mAutoBrightnessLevelsConfig=" + + Arrays.toString(mAutoBrightnessLevelsConfig)); + pw.println(" mAutoBrightnessLcdBacklightValuesConfig=" + + Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig)); + pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); + + if (Looper.myLooper() == mHandler.getLooper()) { + dumpLocal(pw); + } else { + final StringWriter out = new StringWriter(); + final CountDownLatch latch = new CountDownLatch(1); + Message msg = Message.obtain(mHandler, new Runnable() { + @Override + public void run() { + PrintWriter localpw = new PrintWriter(out); + try { + dumpLocal(localpw); + } finally { + localpw.flush(); + latch.countDown(); + } + } + }); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + try { + latch.await(); + pw.print(out.toString()); + } catch (InterruptedException ex) { + pw.println(); + pw.println("Failed to dump thread state due to interrupted exception!"); + } + } + } + + 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(" mPendingLightSensorDebounceTime=" + + TimeUtils.formatUptime(mPendingLightSensorDebounceTime)); + pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); + pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness); + + 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 onProximityNegative(); + } + + private final class DisplayControllerHandler extends Handler { + public DisplayControllerHandler(Looper looper) { + super(looper); + } + + @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. + } + }; +} diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java new file mode 100644 index 0000000..7e4607e --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerRequest.java @@ -0,0 +1,96 @@ +/* + * 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 android.os.PowerManager; + +/** + * Describes the requested power state of the display. + * + * This object is intended to describe the general characteristics of the + * power state, such as whether the screen should be on or off and the current + * brightness controls leaving the {@link DisplayPowerController} to manage the + * details of how the transitions between states should occur. The goal is for + * the {@link PowerManagerService} to focus on the global power state and not + * have to micro-manage screen off animations, auto-brightness and other effects. + */ +final class DisplayPowerRequest { + public static final int SCREEN_STATE_OFF = 0; + public static final int SCREEN_STATE_DIM = 1; + public static final int SCREEN_STATE_BRIGHT = 2; + + // The requested minimum screen power state: off, dim or bright. + public int screenState; + + // If true, the proximity sensor overrides the screen state when an object is + // nearby, turning it off temporarily until the object is moved away. + public boolean useProximitySensor; + + // The desired screen brightness in the range 0 (minimum / off) to 255 (brightest). + // The display power controller may choose to clamp the brightness. + // When auto-brightness is enabled, this field should specify a nominal default + // value to use while waiting for the light sensor to report enough data. + public int screenBrightness; + + // If true, enables automatic brightness control. + public boolean useAutoBrightness; + + public DisplayPowerRequest() { + screenState = SCREEN_STATE_BRIGHT; + useProximitySensor = false; + screenBrightness = PowerManager.BRIGHTNESS_ON; + useAutoBrightness = false; + } + + public DisplayPowerRequest(DisplayPowerRequest other) { + copyFrom(other); + } + + public void copyFrom(DisplayPowerRequest other) { + screenState = other.screenState; + useProximitySensor = other.useProximitySensor; + screenBrightness = other.screenBrightness; + useAutoBrightness = other.useAutoBrightness; + } + + @Override + public boolean equals(Object o) { + return o instanceof DisplayPowerRequest + && equals((DisplayPowerRequest)o); + } + + public boolean equals(DisplayPowerRequest other) { + return other != null + && screenState == other.screenState + && useProximitySensor == other.useProximitySensor + && screenBrightness == other.screenBrightness + && useAutoBrightness == other.useAutoBrightness; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + @Override + public String toString() { + return "screenState=" + screenState + + ", useProximitySensor=" + useProximitySensor + + ", screenBrightness=" + screenBrightness + + ", useAutoBrightness=" + useAutoBrightness; + } +} diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java new file mode 100644 index 0000000..ad242c0 --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -0,0 +1,269 @@ +/* + * 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 android.os.Looper; +import android.os.PowerManager; +import android.util.FloatProperty; +import android.util.IntProperty; +import android.util.Slog; +import android.view.Choreographer; + +import java.io.PrintWriter; + +/** + * Represents the current display power state and realizes it. + * + * This component is similar in nature to a {@link View} except that it describes + * the properties of a display. When properties are changed, the component + * invalidates itself and posts a callback to the {@link Choreographer} to + * apply the changes. This mechanism enables the display power state to be + * animated smoothly by the animation framework. + * + * This component must only be created or accessed by the {@link Looper} thread + * that belongs to the {@link DisplayPowerController}. + * + * We don't need to worry about holding a suspend blocker here because the + * {@link DisplayPowerController} does that for us whenever there is a pending invalidate. + */ +final class DisplayPowerState { + private static final String TAG = "DisplayPowerState"; + + private static boolean DEBUG = false; + + private static final int DIRTY_SCREEN_ON = 1 << 0; + private static final int DIRTY_ELECTRON_BEAM = 1 << 1; + private static final int DIRTY_BRIGHTNESS = 1 << 2; + + private static final int DIRTY_ALL = 0xffffffff; + + private final Choreographer mChoreographer; + private final ElectronBeam mElectronBeam; + private final PhotonicModulator mScreenBrightnessModulator; + + private int mDirty; + private boolean mScreenOn; + private float mElectronBeamLevel; + private int mScreenBrightness; + + private Runnable mCleanListener; + + public DisplayPowerState(ElectronBeam electronBean, + PhotonicModulator screenBrightnessModulator) { + mChoreographer = Choreographer.getInstance(); + mElectronBeam = electronBean; + mScreenBrightnessModulator = screenBrightnessModulator; + + mScreenOn = true; + mElectronBeamLevel = 1.0f; + mScreenBrightness = PowerManager.BRIGHTNESS_ON; + invalidate(DIRTY_ALL); + } + + public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL = + new FloatProperty<DisplayPowerState>("electronBeamLevel") { + @Override + public void setValue(DisplayPowerState object, float value) { + object.setElectronBeamLevel(value); + } + + @Override + public Float get(DisplayPowerState object) { + return object.getElectronBeamLevel(); + } + }; + + public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = + new IntProperty<DisplayPowerState>("screenBrightness") { + @Override + public void setValue(DisplayPowerState object, int value) { + object.setScreenBrightness(value); + } + + @Override + public Integer get(DisplayPowerState object) { + return object.getScreenBrightness(); + } + }; + + /** + * Sets whether the screen is on or off. + */ + public void setScreenOn(boolean on) { + if (mScreenOn != on) { + if (DEBUG) { + Slog.d(TAG, "setScreenOn: on=" + on); + } + + mScreenOn = on; + invalidate(DIRTY_SCREEN_ON); + } + } + + /** + * Returns true if the screen is on. + */ + public boolean isScreenOn() { + return mScreenOn; + } + + /** + * Prepares the electron beam to turn on or off. + * This method should be called before starting an animation because it + * can take a fair amount of time to prepare the electron beam surface. + * + * @param warmUp True if the electron beam should start warming up. + * @return True if the electron beam was prepared. + */ + public boolean prepareElectronBeam(boolean warmUp) { + boolean success = mElectronBeam.prepare(warmUp); + invalidate(DIRTY_ELECTRON_BEAM); + return success; + } + + /** + * Dismisses the electron beam surface. + */ + public void dismissElectronBeam() { + mElectronBeam.dismiss(); + } + + /** + * Sets the level of the electron beam steering current. + * + * The display is blanked when the level is 0.0. In normal use, the electron + * beam should have a value of 1.0. The electron beam is unstable in between + * these states and the picture quality may be compromised. For best effect, + * the electron beam should be warmed up or cooled off slowly. + * + * Warning: Electron beam emits harmful radiation. Avoid direct exposure to + * skin or eyes. + * + * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). + */ + public void setElectronBeamLevel(float level) { + if (mElectronBeamLevel != level) { + if (DEBUG) { + Slog.d(TAG, "setElectronBeamLevel: level=" + level); + } + + mElectronBeamLevel = level; + invalidate(DIRTY_ELECTRON_BEAM); + } + } + + /** + * Gets the level of the electron beam steering current. + */ + public float getElectronBeamLevel() { + return mElectronBeamLevel; + } + + /** + * Sets the display brightness. + * + * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). + */ + public void setScreenBrightness(int brightness) { + if (mScreenBrightness != brightness) { + if (DEBUG) { + Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); + } + + mScreenBrightness = brightness; + invalidate(DIRTY_BRIGHTNESS); + } + } + + /** + * Gets the screen brightness. + */ + public int getScreenBrightness() { + return mScreenBrightness; + } + + /** + * Returns true if no properties have been invalidated. + * Otherwise, returns false and promises to invoke the specified listener + * when the properties have all been applied. + * The listener always overrides any previously set listener. + */ + public boolean waitUntilClean(Runnable listener) { + if (mDirty != 0) { + mCleanListener = listener; + return false; + } else { + mCleanListener = null; + return true; + } + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Display Power State:"); + pw.println(" mDirty=" + Integer.toHexString(mDirty)); + pw.println(" mScreenOn=" + mScreenOn); + pw.println(" mScreenBrightness=" + mScreenBrightness); + pw.println(" mElectronBeamLevel=" + mElectronBeamLevel); + + mElectronBeam.dump(pw); + } + + private void invalidate(int dirty) { + if (mDirty == 0) { + mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, + mTraversalRunnable, null); + } + + mDirty |= dirty; + } + + private void apply() { + if (mDirty != 0) { + if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { + PowerManagerService.nativeSetScreenState(false); + } + + if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) { + mElectronBeam.draw(mElectronBeamLevel); + } + + if ((mDirty & DIRTY_BRIGHTNESS) != 0) { + mScreenBrightnessModulator.setBrightness(mScreenBrightness); + } + + if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { + PowerManagerService.nativeSetScreenState(true); + } + + mDirty = 0; + + if (mCleanListener != null) { + mCleanListener.run(); + } + } + } + + private final Runnable mTraversalRunnable = new Runnable() { + @Override + public void run() { + if (mDirty != 0) { + apply(); + } + } + }; +} diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java new file mode 100644 index 0000000..5472148 --- /dev/null +++ b/services/java/com/android/server/power/ElectronBeam.java @@ -0,0 +1,652 @@ +/* + * 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 android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.opengl.GLES10; +import android.opengl.GLUtils; +import android.os.Looper; +import android.os.Process; +import android.util.FloatMath; +import android.util.Slog; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; +import android.view.SurfaceSession; +import android.view.WindowManagerImpl; + +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * Bzzzoooop! *crackle* + * + * Animates a screen transition from on to off or off to on by applying + * some GL transformations to a screenshot. + * + * This component must only be created or accessed by the {@link Looper} thread + * that belongs to the {@link DisplayPowerController}. + */ +final class ElectronBeam { + private static final String TAG = "ElectronBeam"; + + private static final boolean DEBUG = false; + + // The layer for the electron beam surface. + // This is currently hardcoded to be one layer above the boot animation. + private static final int ELECTRON_BEAM_LAYER = 0x40000001; + + // The relative proportion of the animation to spend performing + // the horizontal stretch effect. The remainder is spent performing + // the vertical stretch effect. + private static final float HSTRETCH_DURATION = 0.3f; + private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION; + + // Set to true when the animation context has been fully prepared. + private boolean mPrepared; + private boolean mWarmUp; + + private final DisplayInfo mDisplayInfo = new DisplayInfo(); + private int mDisplayLayerStack; // layer stack associated with primary display + private int mDisplayRotation; + private int mDisplayWidth; // real width, not rotated + private int mDisplayHeight; // real height, not rotated + private SurfaceSession mSurfaceSession; + private Surface mSurface; + private EGLDisplay mEglDisplay; + private EGLConfig mEglConfig; + private EGLContext mEglContext; + private EGLSurface mEglSurface; + private boolean mSurfaceVisible; + + // Texture names. We only use one texture, which contains the screenshot. + private final int[] mTexNames = new int[1]; + private boolean mTexNamesGenerated; + + // Vertex and corresponding texture coordinates. + // We have 4 2D vertices, so 8 elements. The vertices form a quad. + private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); + private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); + + public ElectronBeam() { + } + + /** + * Warms up the electron beam in preparation for turning on or off. + * This method prepares a GL context, and captures a screen shot. + * + * @param warmUp True if the electron beam is about to be turned on, false if + * it is about to be turned off. + * @return True if the electron beam is ready, false if it is uncontrollable. + */ + public boolean prepare(boolean warmUp) { + if (DEBUG) { + Slog.d(TAG, "prepare: warmUp=" + warmUp); + } + + mWarmUp = warmUp; + + // Get the display size and adjust it for rotation. + Display display = WindowManagerImpl.getDefault().getDefaultDisplay(); + display.getDisplayInfo(mDisplayInfo); + mDisplayLayerStack = display.getDisplayId(); + mDisplayRotation = mDisplayInfo.rotation; + if (mDisplayRotation == Surface.ROTATION_90 + || mDisplayRotation == Surface.ROTATION_270) { + mDisplayWidth = mDisplayInfo.logicalHeight; + mDisplayHeight = mDisplayInfo.logicalWidth; + } else { + mDisplayWidth = mDisplayInfo.logicalWidth; + mDisplayHeight = mDisplayInfo.logicalHeight; + } + + // Prepare the surface for drawing. + if (!createEglContext() + || !createEglSurface() + || !captureScreenshotTextureAndSetViewport()) { + dismiss(); + return false; + } + + mPrepared = true; + return true; + } + + /** + * Dismisses the electron beam animation surface and cleans up. + * + * To prevent stray photons from leaking out after the electron beam has been + * turned off, it is a good idea to defer dismissing the animation until the + * electron beam has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + destroyScreenshotTexture(); + destroyEglSurface(); + mPrepared = false; + } + + /** + * Draws an animation frame showing the electron beam activated at the + * specified level. + * + * @param level The electron beam level. + * @return True if successful. + */ + public boolean draw(float level) { + if (DEBUG) { + Slog.d(TAG, "drawFrame: level=" + level); + } + + if (!attachEglContext()) { + return false; + } + try { + // Clear frame to solid black. + GLES10.glClearColor(0f, 0f, 0f, 1f); + GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT); + + // Draw the frame. + if (level < HSTRETCH_DURATION) { + drawHStretch(1.0f - (level / HSTRETCH_DURATION)); + } else { + drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION)); + } + if (checkGlErrors("drawFrame")) { + return false; + } + + EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); + } finally { + detachEglContext(); + } + + return showEglSurface(); + } + + /** + * Draws a frame where the content of the electron beam is collapsing inwards upon + * itself vertically with red / green / blue channels dispersing and eventually + * merging down to a single horizontal line. + * + * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse. + */ + private void drawVStretch(float stretch) { + // compute interpolation scale factors for each color channel + final float ar = scurve(stretch, 7.5f); + final float ag = scurve(stretch, 8.0f); + final float ab = scurve(stretch, 8.5f); + if (DEBUG) { + Slog.d(TAG, "drawVStretch: stretch=" + stretch + + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab); + } + + // set blending + GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE); + GLES10.glEnable(GLES10.GL_BLEND); + + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // bind texture and set blending for drawing planes + GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); + GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE, + mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glEnable(GLES10.GL_TEXTURE_2D); + GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer); + GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + + // draw the red plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar); + GLES10.glColorMask(true, false, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the green plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColorMask(false, true, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the blue plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab); + GLES10.glColorMask(false, false, true, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up after drawing planes + GLES10.glDisable(GLES10.GL_TEXTURE_2D); + GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + GLES10.glColorMask(true, true, true, true); + + // draw the white highlight (we use the last vertices) + if (!mWarmUp) { + GLES10.glColor4f(ag, ag, ag, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + } + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + GLES10.glDisable(GLES10.GL_BLEND); + } + + /** + * Draws a frame where the electron beam has been stretched out into + * a thin white horizontal line that fades as it expands outwards. + * + * @param stretch The stretch factor. 0.0 is no stretch / no fade, + * 1.0 is maximum stretch / maximum fade. + */ + private void drawHStretch(float stretch) { + // compute interpolation scale factor + final float ag = scurve(stretch, 8.0f); + if (DEBUG) { + Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag); + } + + if (stretch < 1.0f) { + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // draw narrow fading white line + setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + } + } + + private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = dw + (dw * a); + final float h = dh - (dh * a); + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = dw + (dw * a); + final float h = 1.0f; + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) { + if (DEBUG) { + Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h); + } + vtx.put(0, x); + vtx.put(1, y); + vtx.put(2, x); + vtx.put(3, y + h); + vtx.put(4, x + w); + vtx.put(5, y + h); + vtx.put(6, x + w); + vtx.put(7, y); + } + + private boolean captureScreenshotTextureAndSetViewport() { + // TODO: Use a SurfaceTexture to avoid the extra texture upload. + Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight, + 0, ELECTRON_BEAM_LAYER - 1); + if (bitmap == null) { + Slog.e(TAG, "Could not take a screenshot!"); + return false; + } + try { + if (!attachEglContext()) { + return false; + } + try { + if (!mTexNamesGenerated) { + GLES10.glGenTextures(1, mTexNames, 0); + if (checkGlErrors("glGenTextures")) { + return false; + } + mTexNamesGenerated = true; + } + + GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); + if (checkGlErrors("glBindTexture")) { + return false; + } + + float u = 1.0f; + float v = 1.0f; + GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0); + if (checkGlErrors("glTexImage2D, first try", false)) { + // Try a power of two size texture instead. + int tw = nextPowerOfTwo(mDisplayWidth); + int th = nextPowerOfTwo(mDisplayHeight); + int format = GLUtils.getInternalFormat(bitmap); + GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0, + format, tw, th, 0, + format, GLES10.GL_UNSIGNED_BYTE, null); + if (checkGlErrors("glTexImage2D, second try")) { + return false; + } + + GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap); + if (checkGlErrors("glTexSubImage2D")) { + return false; + } + + u = (float)mDisplayWidth / tw; + v = (float)mDisplayHeight / th; + } + + // Set up texture coordinates for a quad. + // We might need to change this if the texture ends up being + // a different size from the display for some reason. + mTexCoordBuffer.put(0, 0f); + mTexCoordBuffer.put(1, v); + mTexCoordBuffer.put(2, 0f); + mTexCoordBuffer.put(3, 0f); + mTexCoordBuffer.put(4, u); + mTexCoordBuffer.put(5, 0f); + mTexCoordBuffer.put(6, u); + mTexCoordBuffer.put(7, v); + + // Set up our viewport. + GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight); + GLES10.glMatrixMode(GLES10.GL_PROJECTION); + GLES10.glLoadIdentity(); + GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1); + GLES10.glMatrixMode(GLES10.GL_MODELVIEW); + GLES10.glLoadIdentity(); + GLES10.glMatrixMode(GLES10.GL_TEXTURE); + GLES10.glLoadIdentity(); + } finally { + detachEglContext(); + } + } finally { + bitmap.recycle(); + } + return true; + } + + private void destroyScreenshotTexture() { + if (mTexNamesGenerated) { + mTexNamesGenerated = false; + if (attachEglContext()) { + try { + GLES10.glDeleteTextures(1, mTexNames, 0); + checkGlErrors("glDeleteTextures"); + } finally { + detachEglContext(); + } + } + } + } + + private boolean createEglContext() { + if (mEglDisplay == null) { + mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { + logEglError("eglGetDisplay"); + return false; + } + + int[] version = new int[2]; + if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { + mEglDisplay = null; + logEglError("eglInitialize"); + return false; + } + } + + if (mEglConfig == null) { + int[] eglConfigAttribList = new int[] { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_NONE + }; + int[] numEglConfigs = new int[1]; + EGLConfig[] eglConfigs = new EGLConfig[1]; + if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0, + eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { + logEglError("eglChooseConfig"); + return false; + } + mEglConfig = eglConfigs[0]; + } + + if (mEglContext == null) { + int[] eglContextAttribList = new int[] { + EGL14.EGL_NONE + }; + mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, + EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0); + if (mEglContext == null) { + logEglError("eglCreateContext"); + return false; + } + } + return true; + } + + /* not used because it is too expensive to create / destroy contexts all of the time + private void destroyEglContext() { + if (mEglContext != null) { + if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) { + logEglError("eglDestroyContext"); + } + mEglContext = null; + } + }*/ + + private boolean createEglSurface() { + if (mSurfaceSession == null) { + mSurfaceSession = new SurfaceSession(); + } + + Surface.openTransaction(); + try { + if (mSurface == null) { + try { + mSurface = new Surface(mSurfaceSession, Process.myPid(), + "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight, + PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN); + } catch (Surface.OutOfResourcesException ex) { + Slog.e(TAG, "Unable to create surface.", ex); + return false; + } + } + + mSurface.setSize(mDisplayWidth, mDisplayHeight); + + switch (mDisplayRotation) { + case Surface.ROTATION_0: + mSurface.setPosition(0, 0); + mSurface.setMatrix(1, 0, 0, 1); + break; + case Surface.ROTATION_90: + mSurface.setPosition(0, mDisplayWidth); + mSurface.setMatrix(0, -1, 1, 0); + break; + case Surface.ROTATION_180: + mSurface.setPosition(mDisplayWidth, mDisplayHeight); + mSurface.setMatrix(-1, 0, 0, -1); + break; + case Surface.ROTATION_270: + mSurface.setPosition(mDisplayHeight, 0); + mSurface.setMatrix(0, 1, -1, 0); + break; + } + } finally { + Surface.closeTransaction(); + } + + if (mEglSurface == null) { + int[] eglSurfaceAttribList = new int[] { + EGL14.EGL_NONE + }; + mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, + eglSurfaceAttribList, 0); + if (mEglSurface == null) { + logEglError("eglCreateWindowSurface"); + return false; + } + } + return true; + } + + private void destroyEglSurface() { + if (mEglSurface != null) { + if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { + logEglError("eglDestroySurface"); + } + mEglSurface = null; + } + + if (mSurface != null) { + Surface.openTransaction(); + try { + mSurface.destroy(); + } finally { + Surface.closeTransaction(); + } + mSurface = null; + mSurfaceVisible = false; + } + } + + private boolean showEglSurface() { + if (!mSurfaceVisible) { + Surface.openTransaction(); + try { + mSurface.setLayer(ELECTRON_BEAM_LAYER); + mSurface.show(); + } finally { + Surface.closeTransaction(); + } + mSurfaceVisible = true; + } + return true; + } + + private boolean attachEglContext() { + if (mEglSurface == null) { + return false; + } + if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + logEglError("eglMakeCurrent"); + return false; + } + return true; + } + + private void detachEglContext() { + if (mEglDisplay != null) { + EGL14.eglMakeCurrent(mEglDisplay, + EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); + } + } + + /** + * Interpolates a value in the range 0 .. 1 along a sigmoid curve + * yielding a result in the range 0 .. 1 scaled such that: + * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1. + */ + private static float scurve(float value, float s) { + // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s). + // Here we take the input datum and shift it by 0.5 so that the + // domain spans the range -0.5 .. 0.5 instead of 0 .. 1. + final float x = value - 0.5f; + + // Next apply the sigmoid function to the scaled value + // which produces a value in the range 0 .. 1 so we subtract + // 0.5 to get a value in the range -0.5 .. 0.5 instead. + final float y = sigmoid(x, s) - 0.5f; + + // To obtain the desired boundary conditions we need to scale + // the result so that it fills a range of -1 .. 1. + final float v = sigmoid(0.5f, s) - 0.5f; + + // And finally remap the value back to a range of 0 .. 1. + return y / v * 0.5f + 0.5f; + } + + private static float sigmoid(float x, float s) { + return 1.0f / (1.0f + FloatMath.exp(-x * s)); + } + + private static int nextPowerOfTwo(int value) { + return 1 << (32 - Integer.numberOfLeadingZeros(value)); + } + + private static FloatBuffer createNativeFloatBuffer(int size) { + ByteBuffer bb = ByteBuffer.allocateDirect(size * 4); + bb.order(ByteOrder.nativeOrder()); + return bb.asFloatBuffer(); + } + + private static void logEglError(String func) { + Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable()); + } + + private static boolean checkGlErrors(String func) { + return checkGlErrors(func, true); + } + + private static boolean checkGlErrors(String func, boolean log) { + boolean hadError = false; + int error; + while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) { + if (log) { + Slog.e(TAG, func + " failed: error " + error, new Throwable()); + } + hadError = true; + } + return hadError; + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Electron Beam State:"); + pw.println(" mPrepared=" + mPrepared); + pw.println(" mWarmUp=" + mWarmUp); + pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); + pw.println(" mDisplayRotation=" + mDisplayRotation); + pw.println(" mDisplayWidth=" + mDisplayWidth); + pw.println(" mDisplayHeight=" + mDisplayHeight); + pw.println(" mSurfaceVisible=" + mSurfaceVisible); + } +} diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java new file mode 100644 index 0000000..37384d2 --- /dev/null +++ b/services/java/com/android/server/power/Notifier.java @@ -0,0 +1,458 @@ +/* + * 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.internal.app.IBatteryStats; +import com.android.server.EventLogTags; + +import android.app.ActivityManagerNative; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.WorkSource; +import android.util.EventLog; +import android.util.Slog; +import android.view.WindowManagerPolicy; +import android.view.WindowManagerPolicy.ScreenOnListener; + +/** + * Sends broadcasts about important power state changes. + * + * This methods of this class may be called by the power manager service while + * its lock is being held. Internally it takes care of sending broadcasts to + * notify other components of the system or applications asynchronously. + * + * The notifier is designed to collapse unnecessary broadcasts when it is not + * possible for the system to have observed an intermediate state. + * + * For example, if the device wakes up, goes to sleep and wakes up again immediately + * before the go to sleep broadcast has been sent, then no broadcast will be + * sent about the system going to sleep and waking up. + */ +final class Notifier { + private static final String TAG = "PowerManagerNotifier"; + + private static final boolean DEBUG = false; + + private static final int POWER_STATE_UNKNOWN = 0; + private static final int POWER_STATE_AWAKE = 1; + private static final int POWER_STATE_ASLEEP = 2; + + private static final int MSG_USER_ACTIVITY = 1; + private static final int MSG_BROADCAST = 2; + + private final Object mLock = new Object(); + + private final Context mContext; + private final IBatteryStats mBatteryStats; + private final SuspendBlocker mSuspendBlocker; + private final WindowManagerPolicy mPolicy; + private final ScreenOnListener mScreenOnListener; + + private final NotifierHandler mHandler; + private final Intent mScreenOnIntent; + private final Intent mScreenOffIntent; + + // The current power state. + private int mActualPowerState; + private int mLastGoToSleepReason; + + // The currently broadcasted power state. This reflects what other parts of the + // system have observed. + private int mBroadcastedPowerState; + private boolean mBroadcastInProgress; + private long mBroadcastStartTime; + + // True if a user activity message should be sent. + private boolean mUserActivityPending; + + public Notifier(Looper looper, Context context, IBatteryStats batteryStats, + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + ScreenOnListener screenOnListener) { + mContext = context; + mBatteryStats = batteryStats; + mSuspendBlocker = suspendBlocker; + mPolicy = policy; + mScreenOnListener = screenOnListener; + + mHandler = new NotifierHandler(looper); + mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); + mScreenOnIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); + mScreenOffIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + } + + /** + * Called when a wake lock is acquired. + */ + public void onWakeLockAcquired(int flags, String tag, int ownerUid, int ownerPid, + WorkSource workSource) { + if (DEBUG) { + Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag + + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + + ", workSource=" + workSource); + } + + if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) { + try { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (workSource != null) { + mBatteryStats.noteStartWakelockFromSource( + workSource, ownerPid, tag, monitorType); + } else { + mBatteryStats.noteStartWakelock( + ownerUid, ownerPid, tag, monitorType); + } + } catch (RemoteException ex) { + // Ignore + } + } + } + + /** + * Called when a wake lock is released. + */ + public void onWakeLockReleased(int flags, String tag, int ownerUid, int ownerPid, + WorkSource workSource) { + if (DEBUG) { + Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag + + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + + ", workSource=" + workSource); + } + + if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) { + try { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (workSource != null) { + mBatteryStats.noteStopWakelockFromSource( + workSource, ownerPid, tag, monitorType); + } else { + mBatteryStats.noteStopWakelock( + ownerUid, ownerPid, tag, monitorType); + } + } catch (RemoteException ex) { + // Ignore + } + } + } + + private static boolean isWakeLockAlreadyReportedToBatteryStats(String tag, int uid) { + // The window manager already takes care of reporting battery stats associated + // with the use of the KEEP_SCREEN_ON_FLAG. + // TODO: Use a WorkSource to handle this situation instead of hardcoding it here. + return uid == Process.SYSTEM_UID + && tag.equals(PowerManager.KEEP_SCREEN_ON_FLAG_TAG); + } + + private static int getBatteryStatsWakeLockMonitorType(int flags) { + switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.PARTIAL_WAKE_LOCK: + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return BatteryStats.WAKE_TYPE_PARTIAL; + default: + return BatteryStats.WAKE_TYPE_FULL; + } + } + + /** + * Called when the screen is turned on. + */ + public void onScreenOn() { + if (DEBUG) { + Slog.d(TAG, "onScreenOn"); + } + + try { + mBatteryStats.noteScreenOn(); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the screen is turned off. + */ + public void onScreenOff() { + if (DEBUG) { + Slog.d(TAG, "onScreenOff"); + } + + try { + mBatteryStats.noteScreenOff(); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the screen changes brightness. + */ + public void onScreenBrightness(int brightness) { + if (DEBUG) { + Slog.d(TAG, "onScreenBrightness: brightness=" + brightness); + } + + try { + mBatteryStats.noteScreenBrightness(brightness); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the device is waking up from sleep and the + * display is about to be turned on. + */ + public void onWakeUpStarted() { + if (DEBUG) { + Slog.d(TAG, "onWakeUpStarted"); + } + + synchronized (mLock) { + if (mActualPowerState != POWER_STATE_AWAKE) { + mActualPowerState = POWER_STATE_AWAKE; + updatePendingBroadcastLocked(); + } + } + } + + /** + * Called when the device has finished waking up from sleep + * and the display has been turned on. + */ + public void onWakeUpFinished() { + if (DEBUG) { + Slog.d(TAG, "onWakeUpFinished"); + } + } + + /** + * Called when the device is going to sleep. + */ + public void onGoToSleepStarted(int reason) { + if (DEBUG) { + Slog.d(TAG, "onGoToSleepStarted"); + } + + synchronized (mLock) { + mLastGoToSleepReason = reason; + } + } + + /** + * Called when the device has finished going to sleep and the + * display has been turned off. + * + * This is a good time to make transitions that we don't want the user to see, + * such as bringing the key guard to focus. There's no guarantee for this, + * however because the user could turn the device on again at any time. + * Some things may need to be protected by other mechanisms that defer screen on. + */ + public void onGoToSleepFinished() { + if (DEBUG) { + Slog.d(TAG, "onGoToSleepFinished"); + } + + synchronized (mLock) { + if (mActualPowerState != POWER_STATE_ASLEEP) { + mActualPowerState = POWER_STATE_ASLEEP; + if (mUserActivityPending) { + mUserActivityPending = false; + mHandler.removeMessages(MSG_USER_ACTIVITY); + } + updatePendingBroadcastLocked(); + } + } + } + + /** + * Called when there has been user activity. + */ + public void onUserActivity(int event, int uid) { + if (DEBUG) { + Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid); + } + + try { + mBatteryStats.noteUserActivity(uid, event); + } catch (RemoteException ex) { + // Ignore + } + + synchronized (mLock) { + if (!mUserActivityPending) { + mUserActivityPending = true; + Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + } + + private void updatePendingBroadcastLocked() { + if (!mBroadcastInProgress + && mActualPowerState != POWER_STATE_UNKNOWN + && mActualPowerState != mBroadcastedPowerState) { + mBroadcastInProgress = true; + mSuspendBlocker.acquire(); + Message msg = mHandler.obtainMessage(MSG_BROADCAST); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + + private void sendUserActivity() { + synchronized (mLock) { + if (!mUserActivityPending) { + return; + } + mUserActivityPending = false; + } + + mPolicy.userActivity(); + } + + private void sendNextBroadcast() { + final int powerState; + final int goToSleepReason; + synchronized (mLock) { + if (mActualPowerState == POWER_STATE_UNKNOWN + || mActualPowerState == mBroadcastedPowerState) { + mBroadcastInProgress = false; + mSuspendBlocker.release(); + return; + } + + powerState = mActualPowerState; + goToSleepReason = mLastGoToSleepReason; + + mBroadcastedPowerState = powerState; + mBroadcastStartTime = SystemClock.uptimeMillis(); + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1); + + if (powerState == POWER_STATE_AWAKE) { + sendWakeUpBroadcast(); + } else { + sendGoToSleepBroadcast(goToSleepReason); + } + } + + private void sendWakeUpBroadcast() { + if (DEBUG) { + Slog.d(TAG, "Sending wake up broadcast."); + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); + + mPolicy.screenTurningOn(mScreenOnListener); + try { + ActivityManagerNative.getDefault().wakingUp(); + } catch (RemoteException e) { + // ignore it + } + + if (ActivityManagerNative.isSystemReady()) { + mContext.sendOrderedBroadcast(mScreenOnIntent, null, + mWakeUpBroadcastDone, mHandler, 0, null, null); + } else { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1); + sendNextBroadcast(); + } + } + + private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1, + SystemClock.uptimeMillis() - mBroadcastStartTime, 1); + sendNextBroadcast(); + } + }; + + private void sendGoToSleepBroadcast(int reason) { + if (DEBUG) { + Slog.d(TAG, "Sending go to sleep broadcast."); + } + + int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER; + switch (reason) { + case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: + why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN; + break; + case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; + break; + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0); + + mPolicy.screenTurnedOff(why); + try { + ActivityManagerNative.getDefault().goingToSleep(); + } catch (RemoteException e) { + // ignore it. + } + + if (ActivityManagerNative.isSystemReady()) { + mContext.sendOrderedBroadcast(mScreenOffIntent, null, + mGoToSleepBroadcastDone, mHandler, 0, null, null); + } else { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1); + sendNextBroadcast(); + } + } + + private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, + SystemClock.uptimeMillis() - mBroadcastStartTime, 1); + sendNextBroadcast(); + } + }; + + private final class NotifierHandler extends Handler { + public NotifierHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_USER_ACTIVITY: + sendUserActivity(); + break; + + case MSG_BROADCAST: + sendNextBroadcast(); + break; + } + } + } +} diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java new file mode 100644 index 0000000..f7c9c7d --- /dev/null +++ b/services/java/com/android/server/power/PhotonicModulator.java @@ -0,0 +1,87 @@ +/* + * 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 java.util.concurrent.Executor; + +/** + * Sets the value of a light asynchronously. + * + * This is done to avoid blocking the looper on devices for which + * setting the backlight brightness is especially slow. + */ +final class PhotonicModulator { + private static final int UNKNOWN_LIGHT_VALUE = -1; + + private final Object mLock = new Object(); + + private final LightsService.Light mLight; + private final Executor mExecutor; + private final SuspendBlocker mSuspendBlocker; + + private boolean mPendingChange; + private int mPendingLightValue; + private int mActualLightValue; + + public PhotonicModulator(Executor executor, LightsService.Light light, + SuspendBlocker suspendBlocker) { + mExecutor = executor; + mLight = light; + mSuspendBlocker = suspendBlocker; + mPendingLightValue = UNKNOWN_LIGHT_VALUE; + mActualLightValue = UNKNOWN_LIGHT_VALUE; + } + + /** + * Asynchronously sets the backlight brightness. + * + * @param lightValue The new light value, from 0 to 255. + */ + public void setBrightness(int lightValue) { + synchronized (mLock) { + if (lightValue != mPendingLightValue) { + mPendingLightValue = lightValue; + if (!mPendingChange) { + mPendingChange = true; + mSuspendBlocker.acquire(); + mExecutor.execute(mTask); + } + } + } + } + + private final Runnable mTask = new Runnable() { + @Override + public void run() { + for (;;) { + final int newLightValue; + synchronized (mLock) { + newLightValue = mPendingLightValue; + if (newLightValue == mActualLightValue) { + mSuspendBlocker.release(); + mPendingChange = false; + return; + } + mActualLightValue = newLightValue; + } + mLight.setBrightness(newLightValue); + } + } + }; +} diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 2630239..2d91e6c 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -21,2767 +21,1432 @@ import com.android.server.BatteryService; import com.android.server.EventLogTags; import com.android.server.LightsService; import com.android.server.Watchdog; -import com.android.server.am.BatteryStatsService; +import com.android.server.am.ActivityManagerService; import com.android.server.display.DisplayManagerService; -import android.app.ActivityManagerNative; -import android.app.IActivityManager; +import android.Manifest; import android.content.BroadcastReceiver; -import android.content.ContentQueryMap; import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; -import android.database.Cursor; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.hardware.SystemSensorManager; +import android.net.Uri; import android.os.BatteryManager; -import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IPowerManager; -import android.os.LocalPowerManager; +import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings; +import android.service.dreams.IDreamManager; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.TimeUtils; import android.view.WindowManagerPolicy; -import static android.view.WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR; -import static android.provider.Settings.System.DIM_SCREEN; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; -import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN; -import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE; -import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Observable; -import java.util.Observer; -public class PowerManagerService extends IPowerManager.Stub - implements LocalPowerManager, Watchdog.Monitor { - private static final int NOMINAL_FRAME_TIME_MS = 1000/60; +import libcore.util.Objects; +/** + * The power manager service is responsible for coordinating power management + * functions on the device. + */ +public final class PowerManagerService extends IPowerManager.Stub + implements Watchdog.Monitor { private static final String TAG = "PowerManagerService"; - static final String PARTIAL_NAME = "PowerManagerService"; - // could be either static or controllable at runtime private static final boolean DEBUG = false; - private static final boolean DEBUG_PROXIMITY_SENSOR = (false || DEBUG); - private static final boolean DEBUG_LIGHT_SENSOR = (false || DEBUG); - private static final boolean DEBUG_LIGHT_ANIMATION = (false || DEBUG); - private static final boolean DEBUG_SCREEN_ON = false; - - // Wake lock that ensures that the CPU is running. The screen might not be on. - private static final int PARTIAL_WAKE_LOCK_ID = 1; - - // Wake lock that ensures that the screen is on. - private static final int FULL_WAKE_LOCK_ID = 2; - - private static final boolean LOG_PARTIAL_WL = false; + private static final boolean DEBUG_SPEW = DEBUG && true; + + // Message: Sent when a user activity timeout occurs to update the power state. + private static final int MSG_USER_ACTIVITY_TIMEOUT = 1; + // Message: Sent when the device enters or exits a napping or dreaming state. + private static final int MSG_SANDMAN = 2; + + // Dirty bit: mWakeLocks changed + private static final int DIRTY_WAKE_LOCKS = 1 << 0; + // Dirty bit: mWakefulness changed + private static final int DIRTY_WAKEFULNESS = 1 << 1; + // Dirty bit: user activity was poked or may have timed out + private static final int DIRTY_USER_ACTIVITY = 1 << 2; + // Dirty bit: actual display power state was updated asynchronously + private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3; + // Dirty bit: mBootCompleted changed + private static final int DIRTY_BOOT_COMPLETED = 1 << 4; + // Dirty bit: settings changed + private static final int DIRTY_SETTINGS = 1 << 5; + // Dirty bit: mIsPowered changed + private static final int DIRTY_IS_POWERED = 1 << 6; + // Dirty bit: mStayOn changed + private static final int DIRTY_STAY_ON = 1 << 7; + // Dirty bit: battery state changed + private static final int DIRTY_BATTERY_STATE = 1 << 8; + + // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). + // The screen should be off or in the process of being turned off by the display controller. + private static final int WAKEFULNESS_ASLEEP = 0; + // Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep(). + // When the user activity timeout expires, the device may start napping. + private static final int WAKEFULNESS_AWAKE = 1; + // Wakefulness: The device is napping. It is deciding whether to dream or go to sleep + // but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which + // ends the nap. User activity may brighten the screen but does not end the nap. + private static final int WAKEFULNESS_NAPPING = 2; + // Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(), + // which ends the dream. The device goes to sleep when goToSleep() is called, when + // the dream ends or when unplugged. + // User activity may brighten the screen but does not end the dream. + private static final int WAKEFULNESS_DREAMING = 3; + + // Summarizes the state of all active wakelocks. + private static final int WAKE_LOCK_CPU = 1 << 0; + private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1; + private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2; + private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3; + private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4; + + // Summarizes the user activity state. + private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; + private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1; + + // Default and minimum screen off timeout in milliseconds. + private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000; + private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000; + + // The screen dim duration, in seconds. + // This is subtracted from the end of the screen off timeout so the + // minimum screen off timeout should be longer than this. + private static final int SCREEN_DIM_DURATION = 7 * 1000; - private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK - | PowerManager.SCREEN_DIM_WAKE_LOCK - | PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.FULL_WAKE_LOCK - | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; - - // time since last state: time since last event: - // The short keylight delay comes from secure settings; this is the default. - private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec - private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec - private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec - private static final int LONG_DIM_TIME = 7000; // t+N-5 sec - - // How long to wait to debounce light sensor changes in milliseconds - private static final int LIGHT_SENSOR_DELAY = 2000; - - // light sensor events rate in microseconds - private static final int LIGHT_SENSOR_RATE = 1000000; - - // Expansion of range of light values when applying scale from light - // sensor brightness setting, in the [0..255] brightness range. - private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20; - - // Scaling factor of the light sensor brightness setting when applying - // it to the final brightness. - private static final int LIGHT_SENSOR_OFFSET_SCALE = 8; - - // For debouncing the proximity sensor in milliseconds - private static final int PROXIMITY_SENSOR_DELAY = 1000; - - // trigger proximity if distance is less than 5 cm - private static final float PROXIMITY_THRESHOLD = 5.0f; - - // Cached secure settings; see updateSettingsValues() - private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT; - - // Default timeout for screen off, if not found in settings database = 15 seconds. - private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000; - - // Screen brightness should always have a value, but just in case... - private static final int DEFAULT_SCREEN_BRIGHTNESS = 192; - - // Threshold for BRIGHTNESS_LOW_BATTERY (percentage) - // Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD - private static final int LOW_BATTERY_THRESHOLD = 10; - - // flags for setPowerState - private static final int ALL_LIGHTS_OFF = 0x00000000; - private static final int SCREEN_ON_BIT = 0x00000001; - private static final int SCREEN_BRIGHT_BIT = 0x00000002; - private static final int BUTTON_BRIGHT_BIT = 0x00000004; - private static final int KEYBOARD_BRIGHT_BIT = 0x00000008; - private static final int BATTERY_LOW_BIT = 0x00000010; - - // values for setPowerState - - // SCREEN_OFF == everything off - private static final int SCREEN_OFF = 0x00000000; - - // SCREEN_DIM == screen on, screen backlight dim - private static final int SCREEN_DIM = SCREEN_ON_BIT; - - // SCREEN_BRIGHT == screen on, screen backlight bright - private static final int SCREEN_BRIGHT = SCREEN_ON_BIT | SCREEN_BRIGHT_BIT; - - // SCREEN_BUTTON_BRIGHT == screen on, screen and button backlights bright - private static final int SCREEN_BUTTON_BRIGHT = SCREEN_BRIGHT | BUTTON_BRIGHT_BIT; - - // SCREEN_BUTTON_BRIGHT == screen on, screen, button and keyboard backlights bright - private static final int ALL_BRIGHT = SCREEN_BUTTON_BRIGHT | KEYBOARD_BRIGHT_BIT; - - // used for noChangeLights in setPowerState() - private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT; - - // animate screen lights in PowerManager (as opposed to SurfaceFlinger) - boolean mAnimateScreenLights = true; - - static final int ANIM_STEPS = 60; // nominal # of frames at 60Hz - // Slower animation for autobrightness changes - static final int AUTOBRIGHTNESS_ANIM_STEPS = 2 * ANIM_STEPS; - // Even slower animation for autodimness changes. Set to max to effectively disable dimming. - // Note 100 is used to keep the mWindowScaleAnimation scaling below from overflowing an int. - static final int AUTODIMNESS_ANIM_STEPS = Integer.MAX_VALUE / (NOMINAL_FRAME_TIME_MS * 100); - // Number of steps when performing a more immediate brightness change. - static final int IMMEDIATE_ANIM_STEPS = 4; - - // These magic numbers are the initial state of the LEDs at boot. Ideally - // we should read them from the driver, but our current hardware returns 0 - // for the initial value. Oops! - static final int INITIAL_SCREEN_BRIGHTNESS = 255; - static final int INITIAL_BUTTON_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF; - static final int INITIAL_KEYBOARD_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF; - - private final int MY_UID; - private final int MY_PID; - - private boolean mDoneBooting = false; - private boolean mBootCompleted = false; - private boolean mHeadless = false; - private int mStayOnConditions = 0; - private final int[] mBroadcastQueue = new int[] { -1, -1, -1 }; - private final int[] mBroadcastWhy = new int[3]; - private boolean mPreparingForScreenOn = false; - private boolean mSkippedScreenOn = false; - private boolean mInitialized = false; - private int mPartialCount = 0; - private int mPowerState; - // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER, - // WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT or WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR - private int mScreenOffReason; - private int mUserState; - private boolean mKeyboardVisible = false; - private boolean mUserActivityAllowed = true; - private int mProximityWakeLockCount = 0; - private boolean mProximitySensorEnabled = false; - private boolean mProximitySensorActive = false; - private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active - private long mLastProximityEventTime; - private int mScreenOffTimeoutSetting; - private int mMaximumScreenOffTimeout = Integer.MAX_VALUE; - private int mKeylightDelay; - private int mDimDelay; - private int mScreenOffDelay; - private int mWakeLockState; - private long mLastEventTime = 0; - private long mScreenOffTime; - private volatile WindowManagerPolicy mPolicy; - private final LockList mLocks = new LockList(); - private Intent mScreenOffIntent; - private Intent mScreenOnIntent; - private LightsService mLightsService; private Context mContext; - private LightsService.Light mLcdLight; - private LightsService.Light mButtonLight; - private LightsService.Light mKeyboardLight; - private LightsService.Light mAttentionLight; - private UnsynchronizedWakeLock mBroadcastWakeLock; - private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock; - private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock; - private UnsynchronizedWakeLock mPreventScreenOnPartialLock; - private UnsynchronizedWakeLock mProximityPartialLock; - private HandlerThread mHandlerThread; - private Handler mScreenOffHandler; - private Handler mScreenBrightnessHandler; - private Handler mHandler; - private final TimeoutTask mTimeoutTask = new TimeoutTask(); - private ScreenBrightnessAnimator mScreenBrightnessAnimator; - private boolean mWaitingForFirstLightSensor = false; - private boolean mStillNeedSleepNotification; - private boolean mIsPowered = false; - private IActivityManager mActivityService; - private IBatteryStats mBatteryStats; + private LightsService mLightsService; private BatteryService mBatteryService; - private DisplayManagerService mDisplayManagerService; - private SensorManager mSensorManager; - private Sensor mProximitySensor; - private Sensor mLightSensor; - private boolean mLightSensorEnabled; - private float mLightSensorValue = -1; - private boolean mProxIgnoredBecauseScreenTurnedOff = false; - private int mHighestLightSensorValue = -1; - private boolean mLightSensorPendingDecrease = false; - private boolean mLightSensorPendingIncrease = false; - private float mLightSensorPendingValue = -1; - private float mLightSensorAdjustSetting = 0; - private int mLightSensorScreenBrightness = -1; - private int mLightSensorButtonBrightness = -1; - private int mLightSensorKeyboardBrightness = -1; - private boolean mDimScreen = true; - private boolean mIsDocked = false; - private long mNextTimeout; - private volatile int mPokey = 0; - private volatile boolean mPokeAwakeOnSet = false; - private volatile boolean mInitComplete = false; - private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>(); - // mLastScreenOnTime is the time the screen was last turned on - private long mLastScreenOnTime; - private boolean mPreventScreenOn; - private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS; - private int mScreenBrightnessOverride = -1; - private int mButtonBrightnessOverride = -1; - private int mScreenBrightnessDim; - private boolean mUseSoftwareAutoBrightness; - private boolean mAutoBrightessEnabled; - private int[] mAutoBrightnessLevels; - private int[] mLcdBacklightValues; - private int[] mButtonBacklightValues; - private int[] mKeyboardBacklightValues; - private int mLightSensorWarmupTime; - boolean mUnplugTurnsOnScreen; - private int mWarningSpewThrottleCount; - private long mWarningSpewThrottleTime; - private int mAnimationSetting = ANIM_SETTING_OFF; - private float mWindowScaleAnimation; - - // Must match with the ISurfaceComposer constants in C++. - private static final int ANIM_SETTING_ON = 0x01; - private static final int ANIM_SETTING_OFF = 0x10; + private IBatteryStats mBatteryStats; + private HandlerThread mHandlerThread; + private PowerManagerHandler mHandler; + private WindowManagerPolicy mPolicy; + private Notifier mNotifier; + private DisplayPowerController mDisplayPowerController; + private SettingsObserver mSettingsObserver; + private IDreamManager mDreamManager; + private LightsService.Light mAttentionLight; - private native void nativeInit(); - private native void nativeSetPowerState(boolean screenOn, boolean screenBright); - private native void nativeStartSurfaceFlingerAnimation(int mode); - private static native void nativeAcquireWakeLock(int lock, String id); - private static native void nativeReleaseWakeLock(String id); - private static native int nativeSetScreenState(boolean on); - private static native void nativeShutdown(); - private static native void nativeReboot(String reason) throws IOException; + private final Object mLock = new Object(); - /* - static PrintStream mLog; - static { - try { - mLog = new PrintStream("/data/power.log"); - } - catch (FileNotFoundException e) { - android.util.Slog.e(TAG, "Life is hard", e); - } - } - static class Log { - static void d(String tag, String s) { - mLog.println(s); - android.util.Slog.d(tag, s); - } - static void i(String tag, String s) { - mLog.println(s); - android.util.Slog.i(tag, s); - } - static void w(String tag, String s) { - mLog.println(s); - android.util.Slog.w(tag, s); - } - static void e(String tag, String s) { - mLog.println(s); - android.util.Slog.e(tag, s); - } - } - */ + // A bitfield that indicates what parts of the power state have + // changed and need to be recalculated. + private int mDirty; - /** - * This class works around a deadlock between the lock in PowerManager.WakeLock - * and our synchronizing on mLocks. PowerManager.WakeLock synchronizes on its - * mToken object so it can be accessed from any thread, but it calls into here - * with its lock held. This class is essentially a reimplementation of - * PowerManager.WakeLock, but without that extra synchronized block, because we'll - * only call it with our own locks held. - */ - private class UnsynchronizedWakeLock { - int mFlags; - String mTag; - IBinder mToken; - int mCount = 0; - boolean mRefCounted; - boolean mHeld; - - UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) { - mFlags = flags; - mTag = tag; - mToken = new Binder(); - mRefCounted = refCounted; - } + // Indicates whether the device is awake or asleep or somewhere in between. + // This is distinct from the screen power state, which is managed separately. + private int mWakefulness; - public void acquire() { - if (!mRefCounted || mCount++ == 0) { - long ident = Binder.clearCallingIdentity(); - try { - PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, - MY_UID, MY_PID, mTag, null); - mHeld = true; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } + // True if MSG_SANDMAN has been scheduled. + private boolean mSandmanScheduled; - public void release() { - if (!mRefCounted || --mCount == 0) { - PowerManagerService.this.releaseWakeLockLocked(mToken, 0, false); - mHeld = false; - } - if (mCount < 0) { - throw new RuntimeException("WakeLock under-locked " + mTag); - } - } + // Table of all suspend blockers. + // There should only be a few of these. + private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>(); - public boolean isHeld() - { - return mHeld; - } + // Table of all wake locks acquired by applications. + private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>(); - public String toString() { - return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags) - + " mCount=" + mCount + " mHeld=" + mHeld + ")"; - } - } + // A bitfield that summarizes the state of all active wakelocks. + private int mWakeLockSummary; - private final class BatteryReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - boolean wasPowered = mIsPowered; - mIsPowered = mBatteryService.isPowered(); - - if (mIsPowered != wasPowered) { - // update mStayOnWhilePluggedIn wake lock - updateWakeLockLocked(); - - // treat plugging and unplugging the devices as a user activity. - // users find it disconcerting when they unplug the device - // and it shuts off right away. - // to avoid turning on the screen when unplugging, we only trigger - // user activity when screen was already on. - // temporarily set mUserActivityAllowed to true so this will work - // even when the keyguard is on. - // However, you can also set config_unplugTurnsOnScreen to have it - // turn on. Some devices want this because they don't have a - // charging LED. - synchronized (mLocks) { - if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 || - mUnplugTurnsOnScreen) { - forceUserActivityLocked(); - } - } + // If true, instructs the display controller to wait for the proximity sensor to + // go negative before turning the screen on. + private boolean mRequestWaitForNegativeProximity; - // stop the screensaver if we're now unplugged - if (mPolicy != null && wasPowered) { - mPolicy.stopScreenSaver(); - } - } - } - } - } + // Timestamp of the last time the device was awoken or put to sleep. + private long mLastWakeTime; + private long mLastSleepTime; - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - bootCompleted(); - } - } + // True if we need to send a wake up or go to sleep finished notification + // when the display is ready. + private boolean mSendWakeUpFinishedNotificationWhenReady; + private boolean mSendGoToSleepFinishedNotificationWhenReady; - private final class DockReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, - Intent.EXTRA_DOCK_STATE_UNDOCKED); - dockStateChanged(state); - } - } - - /** - * Set the setting that determines whether the device stays on when plugged in. - * The argument is a bit string, with each bit specifying a power source that, - * when the device is connected to that source, causes the device to stay on. - * See {@link android.os.BatteryManager} for the list of power sources that - * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC} - * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB} - * @param val an {@code int} containing the bits that specify which power sources - * should cause the device to stay on. - */ - public void setStayOnSetting(int val) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null); - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); - } + // Timestamp of the last call to user activity. + private long mLastUserActivityTime; + private long mLastUserActivityTimeNoChangeLights; - public void setMaximumScreenOffTimeount(int timeMs) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS, null); - synchronized (mLocks) { - mMaximumScreenOffTimeout = timeMs; - // recalculate everything - setScreenOffTimeoutsLocked(); - } - } + // A bitfield that summarizes the effect of the user activity timer. + // A zero value indicates that the user activity timer has expired. + private int mUserActivitySummary; - int getStayOnConditionsLocked() { - return mMaximumScreenOffTimeout <= 0 || mMaximumScreenOffTimeout == Integer.MAX_VALUE - ? mStayOnConditions : 0; - } + // The desired display power state. The actual state may lag behind the + // requested because it is updated asynchronously by the display power controller. + private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest(); - private class SettingsObserver implements Observer { - private int getInt(String name, int defValue) { - ContentValues values = mSettings.getValues(name); - Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null; - return iVal != null ? iVal : defValue; - } + // The time the screen was last turned off, in elapsedRealtime() timebase. + private long mLastScreenOffEventElapsedRealTime; - private float getFloat(String name, float defValue) { - ContentValues values = mSettings.getValues(name); - Float fVal = values != null ? values.getAsFloat(Settings.System.VALUE) : null; - return fVal != null ? fVal : defValue; - } + // True if the display power state has been fully applied, which means the display + // is actually on or actually off or whatever was requested. + private boolean mDisplayReady; - public void update(Observable o, Object arg) { - synchronized (mLocks) { - // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC - mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN, - BatteryManager.BATTERY_PLUGGED_AC); - updateWakeLockLocked(); + // True if holding a wake-lock to block suspend of the CPU. + private boolean mHoldingWakeLockSuspendBlocker; - // SCREEN_OFF_TIMEOUT, default to 15 seconds - mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT); + // The suspend blocker used to keep the CPU alive when wake locks have been acquired. + private final SuspendBlocker mWakeLockSuspendBlocker; - // DIM_SCREEN - //mDimScreen = getInt(DIM_SCREEN) != 0; + // True if systemReady() has been called. + private boolean mSystemReady; - mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS); - mLightSensorAdjustSetting = 0; //getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0); + // True if boot completed occurred. We keep the screen on until this happens. + private boolean mBootCompleted; - // SCREEN_BRIGHTNESS_MODE, default to manual - setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)); + // True if the device is plugged into a power source. + private boolean mIsPowered; - // recalculate everything - setScreenOffTimeoutsLocked(); + // True if the device should wake up when plugged or unplugged. + private boolean mWakeUpWhenPluggedOrUnpluggedConfig; - mWindowScaleAnimation = getFloat(WINDOW_ANIMATION_SCALE, 1.0f); - final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f); - mAnimationSetting = 0; - if (mWindowScaleAnimation > 0.5f) { - mAnimationSetting |= ANIM_SETTING_OFF; - } - if (transitionScale > 0.5f) { - // Uncomment this if you want the screen-on animation. - // mAnimationSetting |= ANIM_SETTING_ON; - } - } - } - } + // True if dreams are supported on this device. + private boolean mDreamsSupportedConfig; - public PowerManagerService() { - // Hack to get our uid... should have a func for this. - long token = Binder.clearCallingIdentity(); - MY_UID = Process.myUid(); - MY_PID = Process.myPid(); - Binder.restoreCallingIdentity(token); + // True if dreams are enabled by the user. + private boolean mDreamsEnabledSetting; - // assume nothing is on yet - mUserState = mPowerState = 0; + // The screen off timeout setting value in milliseconds. + private int mScreenOffTimeoutSetting; - // Add ourself to the Watchdog monitors. - Watchdog.getInstance().addMonitor(this); + // The maximum allowable screen off timeout according to the device + // administration policy. Overrides other settings. + private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE; - nativeInit(); - } + // The stay on while plugged in setting. + // A bitfield of battery conditions under which to make the screen stay on. + private int mStayOnWhilePluggedInSetting; - private ContentQueryMap mSettings; + // True if the device should stay on. + private boolean mStayOn; - public void init(Context context, LightsService lights, IActivityManager activity, - BatteryService battery, DisplayManagerService displayManagerService) { - mLightsService = lights; - mContext = context; - mActivityService = activity; - mBatteryStats = BatteryStatsService.getService(); - mBatteryService = battery; - mDisplayManagerService = displayManagerService; - - mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT); - mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS); - mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD); - mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); - mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0")); - - mInitComplete = false; - mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread", - Process.THREAD_PRIORITY_DISPLAY); - mScreenBrightnessAnimator.start(); - - synchronized (mScreenBrightnessAnimator) { - while (!mInitComplete) { - try { - mScreenBrightnessAnimator.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } + // Screen brightness setting limits. + private int mScreenBrightnessSettingMinimum; + private int mScreenBrightnessSettingMaximum; + private int mScreenBrightnessSettingDefault; - mInitComplete = false; - mHandlerThread = new HandlerThread("PowerManagerService") { - @Override - protected void onLooperPrepared() { - super.onLooperPrepared(); - initInThread(); - } - }; - mHandlerThread.start(); + // The screen brightness setting, from 0 to 255. + // Use -1 if no value has been set. + private int mScreenBrightnessSetting; - synchronized (mHandlerThread) { - while (!mInitComplete) { - try { - mHandlerThread.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } - - synchronized (mLocks) { - updateNativePowerStateLocked(); - // We make sure to start out with the screen on due to user activity. - // (They did just boot their device, after all.) - forceUserActivityLocked(); - mInitialized = true; - } - } - - void initInThread() { - mHandler = new Handler(); + // The screen brightness mode. + // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants. + private int mScreenBrightnessModeSetting; - mBroadcastWakeLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true); - mStayOnWhilePluggedInScreenDimLock = new UnsynchronizedWakeLock( - PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false); - mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false); - mPreventScreenOnPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false); - mProximityPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false); + // The screen brightness setting override from the window manager + // to allow the current foreground activity to override the brightness. + // Use -1 to disable. + private int mScreenBrightnessOverrideFromWindowManager = -1; - mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); - mScreenOnIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); - mScreenOffIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + // The screen brightness setting override from the settings application + // to temporarily adjust the brightness until next updated, + // Use -1 to disable. + private int mTemporaryScreenBrightnessSettingOverride = -1; - Resources resources = mContext.getResources(); + private native void nativeInit(); + private static native void nativeShutdown(); + private static native void nativeReboot(String reason) throws IOException; - mAnimateScreenLights = resources.getBoolean( - com.android.internal.R.bool.config_animateScreenLights); + private static native void nativeSetPowerState(boolean screenOn, boolean screenBright); + private static native void nativeAcquireSuspendBlocker(String name); + private static native void nativeReleaseSuspendBlocker(String name); - mUnplugTurnsOnScreen = resources.getBoolean( - com.android.internal.R.bool.config_unplugTurnsOnScreen); + static native void nativeSetScreenState(boolean on); - mScreenBrightnessDim = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDim); - - // read settings for auto-brightness - mUseSoftwareAutoBrightness = resources.getBoolean( - com.android.internal.R.bool.config_automatic_brightness_available); - if (mUseSoftwareAutoBrightness) { - mAutoBrightnessLevels = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevels); - mLcdBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); - mButtonBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessButtonBacklightValues); - mKeyboardBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues); - mLightSensorWarmupTime = resources.getInteger( - com.android.internal.R.integer.config_lightSensorWarmupTime); - } - - ContentResolver resolver = mContext.getContentResolver(); - Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null, - "(" + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?)", - new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS, - SCREEN_BRIGHTNESS_MODE, /*SCREEN_AUTO_BRIGHTNESS_ADJ,*/ - WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE}, - null); - mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler); - SettingsObserver settingsObserver = new SettingsObserver(); - mSettings.addObserver(settingsObserver); - - // pretend that the settings changed so we will get their initial state - settingsObserver.update(mSettings, null); - - // register for the battery changed notifications - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - mContext.registerReceiver(new BatteryReceiver(), filter); - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BOOT_COMPLETED); - mContext.registerReceiver(new BootCompletedReceiver(), filter); - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DOCK_EVENT); - mContext.registerReceiver(new DockReceiver(), filter); - - // Listen for secure settings changes - mContext.getContentResolver().registerContentObserver( - Settings.Secure.CONTENT_URI, true, - new ContentObserver(new Handler()) { - public void onChange(boolean selfChange) { - updateSettingsValues(); - } - }); - updateSettingsValues(); - - synchronized (mHandlerThread) { - mInitComplete = true; - mHandlerThread.notifyAll(); + public PowerManagerService() { + synchronized (mLock) { + mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); + mWakeLockSuspendBlocker.acquire(); + mHoldingWakeLockSuspendBlocker = true; + mWakefulness = WAKEFULNESS_AWAKE; } - } - /** - * Low-level function turn the device off immediately, without trying - * to be clean. Most people should use - * {@link com.android.server.power.internal.app.ShutdownThread} for a clean shutdown. - */ - public static void lowLevelShutdown() { - nativeShutdown(); + nativeInit(); } /** - * Low-level function to reboot the device. - * - * @param reason code to pass to the kernel (e.g. "recovery"), or null. - * @throws IOException if reboot fails for some reason (eg, lack of - * permission) + * Initialize the power manager. + * Must be called before any other functions within the power manager are called. */ - public static void lowLevelReboot(String reason) throws IOException { - nativeReboot(reason); + public void init(Context context, LightsService ls, + ActivityManagerService am, BatteryService bs, IBatteryStats bss, + DisplayManagerService dm) { + mContext = context; + mLightsService = ls; + mBatteryService = bs; + mBatteryStats = bss; + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); + + Watchdog.getInstance().addMonitor(this); } - private class WakeLock implements IBinder.DeathRecipient - { - WakeLock(int f, IBinder b, String t, int u, int p) { - super(); - flags = f; - binder = b; - tag = t; - uid = u == MY_UID ? Process.SYSTEM_UID : u; - pid = p; - if (u != MY_UID || ( - !"KEEP_SCREEN_ON_FLAG".equals(tag) - && !"KeyInputQueue".equals(tag))) { - monitorType = (f & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK - ? BatteryStats.WAKE_TYPE_PARTIAL - : BatteryStats.WAKE_TYPE_FULL; - } else { - monitorType = -1; - } - try { - b.linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); - } - } - public void binderDied() { - synchronized (mLocks) { - releaseWakeLockLocked(this.binder, 0, true); - } - } - final int flags; - final IBinder binder; - final String tag; - final int uid; - final int pid; - final int monitorType; - WorkSource ws; - boolean activated = true; - int minState; - } - - private void updateWakeLockLocked() { - final int stayOnConditions = getStayOnConditionsLocked(); - if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) { - // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set. - mStayOnWhilePluggedInScreenDimLock.acquire(); - mStayOnWhilePluggedInPartialLock.acquire(); - } else { - mStayOnWhilePluggedInScreenDimLock.release(); - mStayOnWhilePluggedInPartialLock.release(); + public void setPolicy(WindowManagerPolicy policy) { + synchronized (mLock) { + mPolicy = policy; } } - private boolean isScreenLock(int flags) - { - int n = flags & LOCK_MASK; - return n == PowerManager.FULL_WAKE_LOCK - || n == PowerManager.SCREEN_BRIGHT_WAKE_LOCK - || n == PowerManager.SCREEN_DIM_WAKE_LOCK - || n == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; + public void systemReady() { + synchronized (mLock) { + mSystemReady = true; + + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); + mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); + mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); + + mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats, + createSuspendBlockerLocked("PowerManagerService.Broadcasts"), + mPolicy, mScreenOnListener); + mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), + mContext, mNotifier, mLightsService, + createSuspendBlockerLocked("PowerManagerService.Display"), + mDisplayPowerControllerCallbacks, mHandler); + + mSettingsObserver = new SettingsObserver(mHandler); + mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION); + + // Register for broadcasts from other components of the system. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mContext.registerReceiver(new BatteryReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(new BootCompletedReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + mContext.registerReceiver(new DockReceiver(), filter); + + // Register for settings changes. + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ENABLED), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver); + + // Go. + readConfigurationLocked(); + updateSettingsLocked(); + mDirty |= DIRTY_BATTERY_STATE; + updatePowerStateLocked(); + } + } + + private void readConfigurationLocked() { + final Resources resources = mContext.getResources(); + + mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean( + com.android.internal.R.bool.config_unplugTurnsOnScreen); + mDreamsSupportedConfig = resources.getBoolean( + com.android.internal.R.bool.config_enableDreams); } - void enforceWakeSourcePermission(int uid, int pid) { - if (uid == Process.myUid()) { - return; + private void updateSettingsLocked() { + final ContentResolver resolver = mContext.getContentResolver(); + + mDreamsEnabledSetting = (Settings.Secure.getInt(resolver, + Settings.Secure.SCREENSAVER_ENABLED, 0) != 0); + mScreenOffTimeoutSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT); + mStayOnWhilePluggedInSetting = Settings.System.getInt(resolver, + Settings.System.STAY_ON_WHILE_PLUGGED_IN, + BatteryManager.BATTERY_PLUGGED_AC); + + final int oldScreenBrightnessSetting = mScreenBrightnessSetting; + mScreenBrightnessSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault); + if (oldScreenBrightnessSetting != mScreenBrightnessSetting) { + mTemporaryScreenBrightnessSettingOverride = -1; } - mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, - pid, uid, null); + + mScreenBrightnessModeSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + mDirty |= DIRTY_SETTINGS; } - public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - if (uid != Process.myUid()) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + private void handleSettingsChangedLocked() { + updateSettingsLocked(); + updatePowerStateLocked(); + } + + @Override // Binder call + public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); } - if (ws != null) { - enforceWakeSourcePermission(uid, pid); + PowerManager.validateWakeLockParameters(flags, tag); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + if (ws != null && ws.size() != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + } else { + ws = null; } - long ident = Binder.clearCallingIdentity(); + + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, uid, pid, tag, ws); - } + acquireWakeLockInternal(lock, flags, tag, ws, uid, pid); } finally { Binder.restoreCallingIdentity(ident); } } - void noteStartWakeLocked(WakeLock wl, WorkSource ws) { - if (wl.monitorType >= 0) { - long origId = Binder.clearCallingIdentity(); - try { - if (ws != null) { - mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag, - wl.monitorType); - } else { - mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); + private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws, + int uid, int pid) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock) + + ", flags=0x" + Integer.toHexString(flags) + + ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid); + } + + WakeLock wakeLock; + int index = findWakeLockIndexLocked(lock); + if (index >= 0) { + wakeLock = mWakeLocks.get(index); + if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) { + // Update existing wake lock. This shouldn't happen but is harmless. + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.updateProperties(flags, tag, ws, uid, pid); + notifyWakeLockAcquiredLocked(wakeLock); } - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(origId); + } else { + wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid); + try { + lock.linkToDeath(wakeLock, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + notifyWakeLockAcquiredLocked(wakeLock); + mWakeLocks.add(wakeLock); } + + applyWakeLockFlagsOnAcquireLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); } } - void noteStopWakeLocked(WakeLock wl, WorkSource ws) { - if (wl.monitorType >= 0) { - long origId = Binder.clearCallingIdentity(); - try { - if (ws != null) { - mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag, - wl.monitorType); - } else { - mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); - } - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(origId); - } + private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); } } - public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag, - WorkSource ws) { - if (DEBUG) { - Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag); + @Override // Binder call + public void releaseWakeLock(IBinder lock, int flags) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); } - if (ws != null && ws.size() == 0) { - ws = null; + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + + final long ident = Binder.clearCallingIdentity(); + try { + releaseWakeLockInternal(lock, flags); + } finally { + Binder.restoreCallingIdentity(ident); } + } - int index = mLocks.getIndex(lock); - WakeLock wl; - boolean newlock; - boolean diffsource; - WorkSource oldsource; - if (index < 0) { - wl = new WakeLock(flags, lock, tag, uid, pid); - switch (wl.flags & LOCK_MASK) - { - case PowerManager.FULL_WAKE_LOCK: - if (mUseSoftwareAutoBrightness) { - wl.minState = SCREEN_BRIGHT; - } else { - wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT); - } - break; - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - wl.minState = SCREEN_BRIGHT; - break; - case PowerManager.SCREEN_DIM_WAKE_LOCK: - wl.minState = SCREEN_DIM; - break; - case PowerManager.PARTIAL_WAKE_LOCK: - case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: - break; - default: - // just log and bail. we're in the server, so don't - // throw an exception. - Slog.e(TAG, "bad wakelock type for lock '" + tag + "' " - + " flags=" + flags); - return; - } - mLocks.addLock(wl); - if (ws != null) { - wl.ws = new WorkSource(ws); + private void releaseWakeLockInternal(IBinder lock, int flags) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock) + + ", flags=0x" + Integer.toHexString(flags)); } - newlock = true; - diffsource = false; - oldsource = null; - } else { - wl = mLocks.get(index); - newlock = false; - oldsource = wl.ws; - if (oldsource != null) { - if (ws == null) { - wl.ws = null; - diffsource = true; - } else { - diffsource = oldsource.diff(ws); - } - } else if (ws != null) { - diffsource = true; - } else { - diffsource = false; + + int index = findWakeLockIndexLocked(lock); + if (index < 0) { + return; } - if (diffsource) { - wl.ws = new WorkSource(ws); + + WakeLock wakeLock = mWakeLocks.get(index); + mWakeLocks.remove(index); + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.mLock.unlinkToDeath(wakeLock, 0); + + if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) { + mRequestWaitForNegativeProximity = true; } + + applyWakeLockFlagsOnReleaseLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); } - if (isScreenLock(flags)) { - // if this causes a wakeup, we reactivate all of the locks and - // set it to whatever they want. otherwise, we modulate that - // by the current state so we never turn it more on than - // it already is. - if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { - mProximityWakeLockCount++; - if (mProximityWakeLockCount == 1) { - enableProximityLockLocked(); - } - } else { - if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { - int oldWakeLockState = mWakeLockState; - mWakeLockState = mLocks.reactivateScreenLocksLocked(); - - // Disable proximity sensor if if user presses power key while we are in the - // "waiting for proximity sensor to go negative" state. - if ((mWakeLockState & SCREEN_ON_BIT) != 0 - && mProximitySensorActive && mProximityWakeLockCount == 0) { - mProximitySensorActive = false; - } + } - if (DEBUG) { - Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState) - + " mWakeLockState=0x" - + Integer.toHexString(mWakeLockState) - + " previous wakeLockState=0x" - + Integer.toHexString(oldWakeLockState)); - } - } else { - if (DEBUG) { - Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState) - + " mLocks.gatherState()=0x" - + Integer.toHexString(mLocks.gatherState()) - + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)); - } - mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState(); - } - setPowerState(mWakeLockState | mUserState); + private void handleWakeLockDeath(WakeLock wakeLock) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock)); } - } - else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { - if (newlock) { - mPartialCount++; - if (mPartialCount == 1) { - if (LOG_PARTIAL_WL) { - EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag); - } - } + + int index = mWakeLocks.indexOf(wakeLock); + if (index < 0) { + return; } - nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME); - } - if (diffsource) { - // If the lock sources have changed, need to first release the - // old ones. - noteStopWakeLocked(wl, oldsource); + mWakeLocks.remove(index); + notifyWakeLockReleasedLocked(wakeLock); + + applyWakeLockFlagsOnReleaseLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); } - if (newlock || diffsource) { - noteStartWakeLocked(wl, ws); + } + + private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) { + userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_OTHER, + PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, + wakeLock.mOwnerUid); } } + @Override // Binder call public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - if (ws != null && ws.size() == 0) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + if (ws != null && ws.size() != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + } else { ws = null; } - if (ws != null) { - enforceWakeSourcePermission(uid, pid); + + final long ident = Binder.clearCallingIdentity(); + try { + updateWakeLockWorkSourceInternal(lock, ws); + } finally { + Binder.restoreCallingIdentity(ident); } - synchronized (mLocks) { - int index = mLocks.getIndex(lock); + } + + private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) { + synchronized (mLock) { + int index = findWakeLockIndexLocked(lock); if (index < 0) { throw new IllegalArgumentException("Wake lock not active"); } - WakeLock wl = mLocks.get(index); - WorkSource oldsource = wl.ws; - wl.ws = ws != null ? new WorkSource(ws) : null; - noteStopWakeLocked(wl, oldsource); - noteStartWakeLocked(wl, ws); + + WakeLock wakeLock = mWakeLocks.get(index); + if (!wakeLock.hasSameWorkSource(ws)) { + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.updateWorkSource(ws); + notifyWakeLockAcquiredLocked(wakeLock); + } } } - public void releaseWakeLock(IBinder lock, int flags) { - int uid = Binder.getCallingUid(); - if (uid != Process.myUid()) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + private int findWakeLockIndexLocked(IBinder lock) { + final int count = mWakeLocks.size(); + for (int i = 0; i < count; i++) { + if (mWakeLocks.get(i).mLock == lock) { + return i; + } } + return -1; + } - synchronized (mLocks) { - releaseWakeLockLocked(lock, flags, false); + private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) { + if (mSystemReady) { + mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, + wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource); } } - private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) { - WakeLock wl = mLocks.removeLock(lock); - if (wl == null) { - return; + private void notifyWakeLockReleasedLocked(WakeLock wakeLock) { + if (mSystemReady) { + mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, + wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource); } + } - if (DEBUG) { - Slog.d(TAG, "releaseWakeLock flags=0x" - + Integer.toHexString(wl.flags) + " tag=" + wl.tag); + @Override // Binder call + public boolean isWakeLockLevelSupported(int level) { + final long ident = Binder.clearCallingIdentity(); + try { + return isWakeLockLevelSupportedInternal(level); + } finally { + Binder.restoreCallingIdentity(ident); } + } - if (isScreenLock(wl.flags)) { - if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { - mProximityWakeLockCount--; - if (mProximityWakeLockCount == 0) { - if (mProximitySensorActive && - ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) { - // wait for proximity sensor to go negative before disabling sensor - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "waiting for proximity sensor to go negative"); - } - } else { - disableProximityLockLocked(); - } - } - } else { - mWakeLockState = mLocks.gatherState(); - // goes in the middle to reduce flicker - if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) { - userActivity(SystemClock.uptimeMillis(), -1, false, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, true); - } - setPowerState(mWakeLockState | mUserState); - } - } - else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { - mPartialCount--; - if (mPartialCount == 0) { - if (LOG_PARTIAL_WL) { - EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag); - } - nativeReleaseWakeLock(PARTIAL_NAME); - } - } - // Unlink the lock from the binder. - wl.binder.unlinkToDeath(wl, 0); + private boolean isWakeLockLevelSupportedInternal(int level) { + synchronized (mLock) { + switch (level) { + case PowerManager.PARTIAL_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.FULL_WAKE_LOCK: + return true; - noteStopWakeLocked(wl, wl.ws); - } + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return mSystemReady && mDisplayPowerController.isProximitySensorAvailable(); - private class PokeLock implements IBinder.DeathRecipient - { - PokeLock(int p, IBinder b, String t) { - super(); - this.pokey = p; - this.binder = b; - this.tag = t; - try { - b.linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); + default: + return false; } } - public void binderDied() { - setPokeLock(0, this.binder, this.tag); - } - int pokey; - IBinder binder; - String tag; - boolean awakeOnSet; } - public void setPokeLock(int pokey, IBinder token, String tag) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - if (token == null) { - Slog.e(TAG, "setPokeLock got null token for tag='" + tag + "'"); - return; - } - - if ((pokey & POKE_LOCK_TIMEOUT_MASK) == POKE_LOCK_TIMEOUT_MASK) { - throw new IllegalArgumentException("setPokeLock can't have both POKE_LOCK_SHORT_TIMEOUT" - + " and POKE_LOCK_MEDIUM_TIMEOUT"); + @Override // Binder call + public void userActivity(long eventTime, int event, int flags) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); } - synchronized (mLocks) { - if (pokey != 0) { - PokeLock p = mPokeLocks.get(token); - int oldPokey = 0; - if (p != null) { - oldPokey = p.pokey; - p.pokey = pokey; - } else { - p = new PokeLock(pokey, token, tag); - mPokeLocks.put(token, p); - } - int oldTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK; - int newTimeout = pokey & POKE_LOCK_TIMEOUT_MASK; - if (((mPowerState & SCREEN_ON_BIT) == 0) && (oldTimeout != newTimeout)) { - p.awakeOnSet = true; - } - } else { - PokeLock rLock = mPokeLocks.remove(token); - if (rLock != null) { - token.unlinkToDeath(rLock, 0); - } - } + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - int oldPokey = mPokey; - int cumulative = 0; - boolean awakeOnSet = false; - for (PokeLock p: mPokeLocks.values()) { - cumulative |= p.pokey; - if (p.awakeOnSet) { - awakeOnSet = true; - } - } - mPokey = cumulative; - mPokeAwakeOnSet = awakeOnSet; + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + userActivityInternal(eventTime, event, flags, uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } - int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK; - int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK; + // Called from native code. + private void userActivityFromNative(long eventTime, int event, int flags) { + userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID); + } - if (oldCumulativeTimeout != newCumulativeTimeout) { - setScreenOffTimeoutsLocked(); - // reset the countdown timer, but use the existing nextState so it doesn't - // change anything - setTimeoutLocked(SystemClock.uptimeMillis(), mTimeoutTask.nextState); + private void userActivityInternal(long eventTime, int event, int flags, int uid) { + synchronized (mLock) { + if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) { + updatePowerStateLocked(); } } } - private static String lockType(int type) - { - switch (type) - { - case PowerManager.FULL_WAKE_LOCK: - return "FULL_WAKE_LOCK "; - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - return "SCREEN_BRIGHT_WAKE_LOCK "; - case PowerManager.SCREEN_DIM_WAKE_LOCK: - return "SCREEN_DIM_WAKE_LOCK "; - case PowerManager.PARTIAL_WAKE_LOCK: - return "PARTIAL_WAKE_LOCK "; - case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: - return "PROXIMITY_SCREEN_OFF_WAKE_LOCK"; - default: - return "??? "; + private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) { + if (DEBUG_SPEW) { + Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime + + ", event=" + event + ", flags=0x" + Integer.toHexString(flags) + + ", uid=" + uid); } - } - - private static String dumpPowerState(int state) { - return (((state & KEYBOARD_BRIGHT_BIT) != 0) - ? "KEYBOARD_BRIGHT_BIT " : "") - + (((state & SCREEN_BRIGHT_BIT) != 0) - ? "SCREEN_BRIGHT_BIT " : "") - + (((state & SCREEN_ON_BIT) != 0) - ? "SCREEN_ON_BIT " : "") - + (((state & BUTTON_BRIGHT_BIT) != 0) - ? "BUTTON_BRIGHT_BIT " : "") - + (((state & BATTERY_LOW_BIT) != 0) - ? "BATTERY_LOW_BIT " : ""); - } - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump PowerManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; + if (eventTime < mLastSleepTime || eventTime < mLastWakeTime + || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) { + return false; } - long now = SystemClock.uptimeMillis(); + mNotifier.onUserActivity(event, uid); - synchronized (mLocks) { - pw.println("Power Manager State:"); - pw.println(" mIsPowered=" + mIsPowered - + " mPowerState=" + mPowerState - + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime) - + " ms"); - pw.println(" mPartialCount=" + mPartialCount); - pw.println(" mWakeLockState=" + dumpPowerState(mWakeLockState)); - pw.println(" mUserState=" + dumpPowerState(mUserState)); - pw.println(" mPowerState=" + dumpPowerState(mPowerState)); - pw.println(" mLocks.gather=" + dumpPowerState(mLocks.gatherState())); - pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now - + " " + ((mNextTimeout-now)/1000) + "s from now"); - pw.println(" mDimScreen=" + mDimScreen - + " mStayOnConditions=" + mStayOnConditions - + " mPreparingForScreenOn=" + mPreparingForScreenOn - + " mSkippedScreenOn=" + mSkippedScreenOn); - pw.println(" mScreenOffReason=" + mScreenOffReason - + " mUserState=" + mUserState); - pw.println(" mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1] - + ',' + mBroadcastQueue[2] + "}"); - pw.println(" mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1] - + ',' + mBroadcastWhy[2] + "}"); - pw.println(" mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet); - pw.println(" mKeyboardVisible=" + mKeyboardVisible - + " mUserActivityAllowed=" + mUserActivityAllowed); - pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay - + " mScreenOffDelay=" + mScreenOffDelay); - pw.println(" mPreventScreenOn=" + mPreventScreenOn - + " mScreenBrightnessOverride=" + mScreenBrightnessOverride - + " mButtonBrightnessOverride=" + mButtonBrightnessOverride); - pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting - + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout); - pw.println(" mLastScreenOnTime=" + mLastScreenOnTime); - pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); - pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); - pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock); - pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock); - pw.println(" mProximityPartialLock=" + mProximityPartialLock); - pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount); - pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); - pw.println(" mProximitySensorActive=" + mProximitySensorActive); - pw.println(" mProximityPendingValue=" + mProximityPendingValue); - pw.println(" mLastProximityEventTime=" + mLastProximityEventTime); - pw.println(" mLightSensorEnabled=" + mLightSensorEnabled - + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting); - pw.println(" mLightSensorValue=" + mLightSensorValue - + " mLightSensorPendingValue=" + mLightSensorPendingValue); - pw.println(" mHighestLightSensorValue=" + mHighestLightSensorValue - + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor); - pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease - + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease); - pw.println(" mLightSensorScreenBrightness=" + mLightSensorScreenBrightness - + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness - + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness); - pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness); - pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled); - mScreenBrightnessAnimator.dump(pw, "mScreenBrightnessAnimator: "); - - int N = mLocks.size(); - pw.println(); - pw.println("mLocks.size=" + N + ":"); - for (int i=0; i<N; i++) { - WakeLock wl = mLocks.get(i); - String type = lockType(wl.flags & LOCK_MASK); - String acquireCausesWakeup = ""; - if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { - acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP "; - } - String activated = ""; - if (wl.activated) { - activated = " activated"; - } - pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup - + activated + " (minState=" + wl.minState + ", uid=" + wl.uid - + ", pid=" + wl.pid + ")"); + if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { + if (eventTime > mLastUserActivityTimeNoChangeLights + && eventTime > mLastUserActivityTime) { + mLastUserActivityTimeNoChangeLights = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; } - - pw.println(); - pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":"); - for (PokeLock p: mPokeLocks.values()) { - pw.println(" poke lock '" + p.tag + "':" - + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0 - ? " POKE_LOCK_IGNORE_TOUCH_EVENTS" : "") - + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0 - ? " POKE_LOCK_SHORT_TIMEOUT" : "") - + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0 - ? " POKE_LOCK_MEDIUM_TIMEOUT" : "")); + } else { + if (eventTime > mLastUserActivityTime) { + mLastUserActivityTime = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; } - - pw.println(); } + return false; } - private void setTimeoutLocked(long now, int nextState) { - setTimeoutLocked(now, -1, nextState); - } - - // If they gave a timeoutOverride it is the number of seconds - // to screen-off. Figure out where in the countdown cycle we - // should jump to. - private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) { - long timeoutOverride = originalTimeoutOverride; - if (mBootCompleted) { - synchronized (mLocks) { - long when = 0; - if (timeoutOverride <= 0) { - switch (nextState) - { - case SCREEN_BRIGHT: - when = now + mKeylightDelay; - break; - case SCREEN_DIM: - if (mDimDelay >= 0) { - when = now + mDimDelay; - break; - } else { - Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); - } - case SCREEN_OFF: - synchronized (mLocks) { - when = now + mScreenOffDelay; - } - break; - default: - when = now; - break; - } - } else { - override: { - if (timeoutOverride <= mScreenOffDelay) { - when = now + timeoutOverride; - nextState = SCREEN_OFF; - break override; - } - timeoutOverride -= mScreenOffDelay; - - if (mDimDelay >= 0) { - if (timeoutOverride <= mDimDelay) { - when = now + timeoutOverride; - nextState = SCREEN_DIM; - break override; - } - timeoutOverride -= mDimDelay; - } + @Override // Binder call + public void wakeUp(long eventTime) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); + } - when = now + timeoutOverride; - nextState = SCREEN_BRIGHT; - } - } - if (DEBUG) { - Slog.d(TAG, "setTimeoutLocked now=" + now - + " timeoutOverride=" + timeoutOverride - + " nextState=" + nextState + " when=" + when); - } + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - mHandler.removeCallbacks(mTimeoutTask); - mTimeoutTask.nextState = nextState; - mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0 - ? (originalTimeoutOverride - timeoutOverride) - : -1; - mHandler.postAtTime(mTimeoutTask, when); - mNextTimeout = when; // for debugging - } + final long ident = Binder.clearCallingIdentity(); + try { + wakeUpInternal(eventTime); + } finally { + Binder.restoreCallingIdentity(ident); } } - private void cancelTimerLocked() - { - mHandler.removeCallbacks(mTimeoutTask); - mTimeoutTask.nextState = -1; + // Called from native code. + private void wakeUpFromNative(long eventTime) { + wakeUpInternal(eventTime); } - private class TimeoutTask implements Runnable - { - int nextState; // access should be synchronized on mLocks - long remainingTimeoutOverride; - public void run() - { - synchronized (mLocks) { - if (DEBUG) { - Slog.d(TAG, "user activity timeout timed out nextState=" + this.nextState); - } - - if (nextState == -1) { - return; - } - - mUserState = this.nextState; - setPowerState(this.nextState | mWakeLockState); - - long now = SystemClock.uptimeMillis(); - - switch (this.nextState) - { - case SCREEN_BRIGHT: - if (mDimDelay >= 0) { - setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM); - break; - } - case SCREEN_DIM: - setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF); - break; - } + private void wakeUpInternal(long eventTime) { + synchronized (mLock) { + if (wakeUpNoUpdateLocked(eventTime)) { + updatePowerStateLocked(); } } } - private void sendNotificationLocked(boolean on, int why) { - if (!mInitialized) { - // No notifications sent until first initialization is done. - // This is so that when we are moving from our initial state - // which looks like the screen was off to it being on, we do not - // go through the process of waiting for the higher-level user - // space to be ready before turning up the display brightness. - // (And also do not send needless broadcasts about the screen.) - return; + private boolean wakeUpNoUpdateLocked(long eventTime) { + if (DEBUG_SPEW) { + Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime); } - if (DEBUG_SCREEN_ON) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "sendNotificationLocked: " + on, here); + if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE + || !mBootCompleted || !mSystemReady) { + return false; } - if (!on) { - mStillNeedSleepNotification = false; + switch (mWakefulness) { + case WAKEFULNESS_ASLEEP: + Slog.i(TAG, "Waking up from sleep..."); + mNotifier.onWakeUpStarted(); + mSendWakeUpFinishedNotificationWhenReady = true; + mSendGoToSleepFinishedNotificationWhenReady = false; + break; + case WAKEFULNESS_DREAMING: + Slog.i(TAG, "Waking up from dream..."); + break; + case WAKEFULNESS_NAPPING: + Slog.i(TAG, "Waking up from nap..."); + break; } - // Add to the queue. - int index = 0; - while (mBroadcastQueue[index] != -1) { - index++; - } - mBroadcastQueue[index] = on ? 1 : 0; - mBroadcastWhy[index] = why; + mLastWakeTime = eventTime; + mWakefulness = WAKEFULNESS_AWAKE; + mDirty |= DIRTY_WAKEFULNESS; - // If we added it position 2, then there is a pair that can be stripped. - // If we added it position 1 and we're turning the screen off, we can strip - // the pair and do nothing, because the screen is already off, and therefore - // keyguard has already been enabled. - // However, if we added it at position 1 and we're turning it on, then position - // 0 was to turn it off, and we can't strip that, because keyguard needs to come - // on, so have to run the queue then. - if (index == 2) { - // While we're collapsing them, if it's going off, and the new reason - // is more significant than the first, then use the new one. - if (!on && mBroadcastWhy[0] > why) { - mBroadcastWhy[0] = why; - } - mBroadcastQueue[0] = on ? 1 : 0; - mBroadcastQueue[1] = -1; - mBroadcastQueue[2] = -1; - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - index = 0; - } - if (index == 1 && !on) { - mBroadcastQueue[0] = -1; - mBroadcastQueue[1] = -1; - index = -1; - // The wake lock was being held, but we're not actually going to do any - // broadcasts, so release the wake lock. - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - - // The broadcast queue has changed; make sure the screen is on if it - // is now possible for it to be. - if (mSkippedScreenOn) { - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - } - - // Now send the message. - if (index >= 0) { - // Acquire the broadcast wake lock before changing the power - // state. It will be release after the broadcast is sent. - // We always increment the ref count for each notification in the queue - // and always decrement when that notification is handled. - mBroadcastWakeLock.acquire(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount); - mHandler.post(mNotificationTask); - } - } - - private WindowManagerPolicy.ScreenOnListener mScreenOnListener = - new WindowManagerPolicy.ScreenOnListener() { - public void onScreenOn() { - synchronized (mLocks) { - if (mPreparingForScreenOn) { - mPreparingForScreenOn = false; - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, - 4, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - } - }; - - private Runnable mNotificationTask = new Runnable() - { - public void run() - { - while (true) { - int value; - int why; - WindowManagerPolicy policy; - synchronized (mLocks) { - value = mBroadcastQueue[0]; - why = mBroadcastWhy[0]; - for (int i=0; i<2; i++) { - mBroadcastQueue[i] = mBroadcastQueue[i+1]; - mBroadcastWhy[i] = mBroadcastWhy[i+1]; - } - policy = getPolicyLocked(); - if (value == 1 && !mPreparingForScreenOn) { - mPreparingForScreenOn = true; - mBroadcastWakeLock.acquire(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, - mBroadcastWakeLock.mCount); - } - } - if (value == 1) { - mScreenOnStart = SystemClock.uptimeMillis(); - - policy.screenTurningOn(mScreenOnListener); - try { - ActivityManagerNative.getDefault().wakingUp(); - } catch (RemoteException e) { - // ignore it - } - - if (DEBUG) { - Slog.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); - } - if (mContext != null && ActivityManagerNative.isSystemReady()) { - mContext.sendOrderedBroadcast(mScreenOnIntent, null, - mScreenOnBroadcastDone, mHandler, 0, null, null); - } else { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, - mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - } - else if (value == 0) { - mScreenOffStart = SystemClock.uptimeMillis(); - - policy.screenTurnedOff(why); - try { - ActivityManagerNative.getDefault().goingToSleep(); - } catch (RemoteException e) { - // ignore it. - } - - if (mContext != null && ActivityManagerNative.isSystemReady()) { - mContext.sendOrderedBroadcast(mScreenOffIntent, null, - mScreenOffBroadcastDone, mHandler, 0, null, null); - } else { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, - mBroadcastWakeLock.mCount); - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - mBroadcastWakeLock.release(); - } - } - } - else { - // If we're in this case, then this handler is running for a previous - // paired transaction. mBroadcastWakeLock will already have been released. - break; - } - } - } - }; - - long mScreenOnStart; - private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1, - SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - }; + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + return true; + } - long mScreenOffStart; - private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, - SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } + @Override // Binder call + public void goToSleep(long eventTime, int reason) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); } - }; - /** - * Prevents the screen from turning on even if it *should* turn on due - * to a subsequent full wake lock being acquired. - * <p> - * This is a temporary hack that allows an activity to "cover up" any - * display glitches that happen during the activity's startup - * sequence. (Specifically, this API was added to work around a - * cosmetic bug in the "incoming call" sequence, where the lock screen - * would flicker briefly before the incoming call UI became visible.) - * TODO: There ought to be a more elegant way of doing this, - * probably by having the PowerManager and ActivityManager - * work together to let apps specify that the screen on/off - * state should be synchronized with the Activity lifecycle. - * <p> - * Note that calling preventScreenOn(true) will NOT turn the screen - * off if it's currently on. (This API only affects *future* - * acquisitions of full wake locks.) - * But calling preventScreenOn(false) WILL turn the screen on if - * it's currently off because of a prior preventScreenOn(true) call. - * <p> - * Any call to preventScreenOn(true) MUST be followed promptly by a call - * to preventScreenOn(false). In fact, if the preventScreenOn(false) - * call doesn't occur within 5 seconds, we'll turn the screen back on - * ourselves (and log a warning about it); this prevents a buggy app - * from disabling the screen forever.) - * <p> - * TODO: this feature should really be controlled by a new type of poke - * lock (rather than an IPowerManager call). - */ - public void preventScreenOn(boolean prevent) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - synchronized (mLocks) { - if (prevent) { - // First of all, grab a partial wake lock to - // make sure the CPU stays on during the entire - // preventScreenOn(true) -> preventScreenOn(false) sequence. - mPreventScreenOnPartialLock.acquire(); - - // Post a forceReenableScreen() call (for 5 seconds in the - // future) to make sure the matching preventScreenOn(false) call - // has happened by then. - mHandler.removeCallbacks(mForceReenableScreenTask); - mHandler.postDelayed(mForceReenableScreenTask, 5000); - - // Finally, set the flag that prevents the screen from turning on. - // (Below, in setPowerState(), we'll check mPreventScreenOn and - // we *won't* call setScreenStateLocked(true) if it's set.) - mPreventScreenOn = true; - } else { - // (Re)enable the screen. - mPreventScreenOn = false; - - // We're "undoing" a the prior preventScreenOn(true) call, so we - // no longer need the 5-second safeguard. - mHandler.removeCallbacks(mForceReenableScreenTask); - - // Forcibly turn on the screen if it's supposed to be on. (This - // handles the case where the screen is currently off because of - // a prior preventScreenOn(true) call.) - if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) { - if (DEBUG) { - Slog.d(TAG, - "preventScreenOn: turning on after a prior preventScreenOn(true)!"); - } - int err = setScreenStateLocked(true); - if (err != 0) { - Slog.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err); - } - } - - // Release the partial wake lock that we held during the - // preventScreenOn(true) -> preventScreenOn(false) sequence. - mPreventScreenOnPartialLock.release(); - } + final long ident = Binder.clearCallingIdentity(); + try { + goToSleepInternal(eventTime, reason); + } finally { + Binder.restoreCallingIdentity(ident); } } - public void setScreenBrightnessOverride(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + // Called from native code. + private void goToSleepFromNative(long eventTime, int reason) { + goToSleepInternal(eventTime, reason); + } - if (DEBUG) Slog.d(TAG, "setScreenBrightnessOverride " + brightness); - synchronized (mLocks) { - if (mScreenBrightnessOverride != brightness) { - mScreenBrightnessOverride = brightness; - if (isScreenOn()) { - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - } + private void goToSleepInternal(long eventTime, int reason) { + synchronized (mLock) { + if (goToSleepNoUpdateLocked(eventTime, reason)) { + updatePowerStateLocked(); } } } - public void setButtonBrightnessOverride(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + private boolean goToSleepNoUpdateLocked(long eventTime, int reason) { + if (DEBUG_SPEW) { + Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason); + } - if (DEBUG) Slog.d(TAG, "setButtonBrightnessOverride " + brightness); - synchronized (mLocks) { - if (mButtonBrightnessOverride != brightness) { - mButtonBrightnessOverride = brightness; - if (isScreenOn()) { - updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT); - } + if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP + || !mBootCompleted || !mSystemReady) { + return false; + } + + switch (reason) { + case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: + Slog.i(TAG, "Going to sleep due to device administration policy..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + Slog.i(TAG, "Going to sleep due to screen timeout..."); + break; + default: + Slog.i(TAG, "Going to sleep by user request..."); + reason = PowerManager.GO_TO_SLEEP_REASON_USER; + break; + } + + mLastSleepTime = eventTime; + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_ASLEEP; + mNotifier.onGoToSleepStarted(reason); + mSendGoToSleepFinishedNotificationWhenReady = true; + mSendWakeUpFinishedNotificationWhenReady = false; + + // Report the number of wake locks that will be cleared by going to sleep. + int numWakeLocksCleared = 0; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; } } + EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared); + return true; } /** - * Sanity-check that gets called 5 seconds after any call to - * preventScreenOn(true). This ensures that the original call - * is followed promptly by a call to preventScreenOn(false). + * Updates the global power state based on dirty bits recorded in mDirty. + * + * This is the main function that performs power state transitions. + * We centralize them here so that we can recompute the power state completely + * each time something important changes, and ensure that we do it the same + * way each time. The point is to gather all of the transition logic here. */ - private void forceReenableScreen() { - // We shouldn't get here at all if mPreventScreenOn is false, since - // we should have already removed any existing - // mForceReenableScreenTask messages... - if (!mPreventScreenOn) { - Slog.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do"); + private void updatePowerStateLocked() { + if (!mSystemReady || mDirty == 0) { return; } - // Uh oh. It's been 5 seconds since a call to - // preventScreenOn(true) and we haven't re-enabled the screen yet. - // This means the app that called preventScreenOn(true) is either - // slow (i.e. it took more than 5 seconds to call preventScreenOn(false)), - // or buggy (i.e. it forgot to call preventScreenOn(false), or - // crashed before doing so.) - - // Log a warning, and forcibly turn the screen back on. - Slog.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! " - + "Forcing the screen back on..."); - preventScreenOn(false); - } + // Phase 0: Basic state updates. + updateIsPoweredLocked(mDirty); + updateStayOnLocked(mDirty); - private Runnable mForceReenableScreenTask = new Runnable() { - public void run() { - forceReenableScreen(); - } - }; + // Phase 1: Update wakefulness. + // Loop because the wake lock and user activity computations are influenced + // by changes in wakefulness. + final long now = SystemClock.uptimeMillis(); + int dirtyPhase2 = 0; + for (;;) { + int dirtyPhase1 = mDirty; + dirtyPhase2 |= dirtyPhase1; + mDirty = 0; - private int setScreenStateLocked(boolean on) { - if (DEBUG_SCREEN_ON) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Set screen state: " + on, e); - } - if (on) { - if (mInitialized && ((mPowerState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn)) { - // If we are turning the screen state on, but the screen - // light is currently off, then make sure that we set the - // light at this point to 0. This is the case where we are - // turning on the screen and waiting for the UI to be drawn - // before showing it to the user. We want the light off - // until it is ready to be shown to the user, not it using - // whatever the last value it had. - // Skip this if the screen is being turned on for the first time - // after boot (mInitialized is false). - if (DEBUG_SCREEN_ON) { - Slog.i(TAG, "Forcing brightness 0: mPowerState=0x" - + Integer.toHexString(mPowerState) - + " mSkippedScreenOn=" + mSkippedScreenOn); - } - mScreenBrightnessAnimator.animateTo(PowerManager.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0); + updateWakeLockSummaryLocked(dirtyPhase1); + updateUserActivitySummaryLocked(now, dirtyPhase1); + if (!updateWakefulnessLocked(dirtyPhase1)) { + break; } } - int err = nativeSetScreenState(on); - if (err == 0) { - mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0); - if (mUseSoftwareAutoBrightness) { - enableLightSensorLocked(on); - if (on) { - // If AutoBrightness is enabled, set the brightness immediately after the - // next sensor value is received. - mWaitingForFirstLightSensor = mAutoBrightessEnabled; - } else { - // make sure button and key backlights are off too - mButtonLight.turnOff(); - mKeyboardLight.turnOff(); - } - } - } - return err; - } - private void setPowerState(int state) - { - setPowerState(state, false, WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT); - } + // Phase 2: Update dreams and display power state. + updateDreamLocked(dirtyPhase2); + updateDisplayPowerStateLocked(dirtyPhase2); - private void setPowerState(int newState, boolean noChangeLights, int reason) - { - synchronized (mLocks) { - int err; + // Phase 3: Send notifications, if needed. + sendPendingNotificationsLocked(); - if (DEBUG) { - Slog.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState) - + " newState=0x" + Integer.toHexString(newState) - + " noChangeLights=" + noChangeLights - + " reason=" + reason); - } - - if (noChangeLights) { - newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK); - } - if (mProximitySensorActive) { - // don't turn on the screen when the proximity sensor lock is held - newState = (newState & ~SCREEN_BRIGHT); - } + // Phase 4: Update suspend blocker. + // Because we might release the last suspend blocker here, we need to make sure + // we finished everything else first! + updateSuspendBlockerLocked(); + } - if (batteryIsLow()) { - newState |= BATTERY_LOW_BIT; - } else { - newState &= ~BATTERY_LOW_BIT; - } - if (newState == mPowerState && mInitialized) { - return; + private void sendPendingNotificationsLocked() { + if (mDisplayReady) { + if (mSendWakeUpFinishedNotificationWhenReady) { + mSendWakeUpFinishedNotificationWhenReady = false; + mNotifier.onWakeUpFinished(); } - - if (!mBootCompleted && !mUseSoftwareAutoBrightness) { - newState |= ALL_BRIGHT; + if (mSendGoToSleepFinishedNotificationWhenReady) { + mSendGoToSleepFinishedNotificationWhenReady = false; + mNotifier.onGoToSleepFinished(); } + } + } - boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0; - boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; - - if (DEBUG) { - Slog.d(TAG, "setPowerState: mPowerState=" + mPowerState - + " newState=" + newState + " noChangeLights=" + noChangeLights); - Slog.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0) - + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0) - + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0) - + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldScreenOn=" + oldScreenOn - + " newScreenOn=" + newScreenOn); - Slog.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0) - + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0)); - } + /** + * Updates the value of mIsPowered. + * Sets DIRTY_IS_POWERED if a change occurred. + */ + private void updateIsPoweredLocked(int dirty) { + if ((dirty & DIRTY_BATTERY_STATE) != 0) { + boolean wasPowered = mIsPowered; + mIsPowered = mBatteryService.isPowered(); - final boolean stateChanged = mPowerState != newState; + if (wasPowered != mIsPowered) { + mDirty |= DIRTY_IS_POWERED; - if (stateChanged && !newScreenOn && reason == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) { - if (mPolicy != null && mIsPowered && mPolicy.isScreenSaverEnabled()) { - if (DEBUG) { - Slog.d(TAG, "setPowerState: running screen saver instead of turning off screen"); - } - if (mPolicy.startScreenSaver()) { - // was successful - return; - } + // Treat plugging and unplugging the devices as a user activity. + // Users find it disconcerting when they plug or unplug the device + // and it shuts off right away. + // Some devices also wake the device when plugged or unplugged because + // they don't have a charging LED. + final long now = SystemClock.uptimeMillis(); + if (mWakeUpWhenPluggedOrUnpluggedConfig) { + wakeUpNoUpdateLocked(now); } + userActivityNoUpdateLocked( + now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); } + } + } + /** + * Updates the value of mStayOn. + * Sets DIRTY_STAY_ON if a change occurred. + */ + private void updateStayOnLocked(int dirty) { + if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) { + if (mStayOnWhilePluggedInSetting != 0 + && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { + mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting); + } else { + mStayOn = false; + } + } + } - if (oldScreenOn != newScreenOn) { - if (newScreenOn) { - // When the user presses the power button, we need to always send out the - // notification that it's going to sleep so the keyguard goes on. But - // we can't do that until the screen fades out, so we don't show the keyguard - // too early. - if (mStillNeedSleepNotification) { - sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER); - } - - // Turn on the screen UNLESS there was a prior - // preventScreenOn(true) request. (Note that the lifetime - // of a single preventScreenOn() request is limited to 5 - // seconds to prevent a buggy app from disabling the - // screen forever; see forceReenableScreen().) - boolean reallyTurnScreenOn = true; - if (DEBUG) { - Slog.d(TAG, "- turning screen on... mPreventScreenOn = " - + mPreventScreenOn); - } - - if (mPreventScreenOn) { - if (DEBUG) { - Slog.d(TAG, "- PREVENTING screen from really turning on!"); + /** + * Updates the value of mWakeLockSummary to summarize the state of all active wake locks. + * Note that most wake-locks are ignored when the system is asleep. + * + * This function must have no other side-effects. + */ + private void updateWakeLockSummaryLocked(int dirty) { + if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) { + mWakeLockSummary = 0; + + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.PARTIAL_WAKE_LOCK: + mWakeLockSummary |= WAKE_LOCK_CPU; + break; + case PowerManager.FULL_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU + | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; } - reallyTurnScreenOn = false; - } - if (reallyTurnScreenOn) { - err = setScreenStateLocked(true); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(getPreferredBrightness()); - mBatteryStats.noteScreenOn(); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); + break; + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT; } - } else { - setScreenStateLocked(false); - // But continue as if we really did turn the screen on... - err = 0; - } - - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason, 0, 0); - if (err == 0) { - sendNotificationLocked(true, -1); - // Update the lights *after* taking care of turning the - // screen on, so we do this after our notifications are - // enqueued and thus will delay turning on the screen light - // until the windows are correctly displayed. - if (stateChanged) { - updateLightsLocked(newState, 0); + break; + case PowerManager.SCREEN_DIM_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM; } - mPowerState |= SCREEN_ON_BIT; - } + break; + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF; + } + break; + } + } - } else { - // Update the lights *before* taking care of turning the - // screen off, so we can initiate any animations that are desired. - mScreenOffReason = reason; - if (stateChanged) { - updateLightsLocked(newState, 0); - } + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness=" + + wakefulnessToString(mWakefulness) + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); + } + } + } - // cancel light sensor task - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - mScreenOffTime = SystemClock.elapsedRealtime(); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenOff(); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - mPowerState &= ~SCREEN_ON_BIT; - if (!mScreenBrightnessAnimator.isAnimating()) { - err = screenOffFinishedAnimatingLocked(reason); + /** + * Updates the value of mUserActivitySummary to summarize the user requested + * state of the system such as whether the screen should be bright or dim. + * Note that user activity is ignored when the system is asleep. + * + * This function must have no other side-effects. + */ + private void updateUserActivitySummaryLocked(long now, int dirty) { + // Update the status of the user activity timeout timer. + if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) { + mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT); + + long nextTimeout = 0; + if (mWakefulness != WAKEFULNESS_ASLEEP) { + final int screenOffTimeout = getScreenOffTimeoutLocked(); + final int screenDimDuration = getScreenDimDurationLocked(); + + mUserActivitySummary = 0; + if (mLastUserActivityTime >= mLastWakeTime) { + nextTimeout = mLastUserActivityTime + + screenOffTimeout - screenDimDuration; + if (now < nextTimeout) { + mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT; } else { - err = 0; - } - - // stop the screensaver if user turned screen off - if (stateChanged && reason == WindowManagerPolicy.OFF_BECAUSE_OF_USER) { - if (mPolicy != null) { - mPolicy.stopScreenSaver(); + nextTimeout = mLastUserActivityTime + screenOffTimeout; + if (now < nextTimeout) { + mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM; } } } - } else if (stateChanged) { - // Screen on/off didn't change, but lights may have. - updateLightsLocked(newState, 0); + if (mUserActivitySummary == 0 + && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) { + nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; + if (now < nextTimeout + && mDisplayPowerRequest.screenState + != DisplayPowerRequest.SCREEN_STATE_OFF) { + mUserActivitySummary = mDisplayPowerRequest.screenState + == DisplayPowerRequest.SCREEN_STATE_BRIGHT ? + USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM; + } + } + if (mUserActivitySummary != 0) { + Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, nextTimeout); + } + } else { + mUserActivitySummary = 0; } - mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK); - - updateNativePowerStateLocked(); + if (DEBUG_SPEW) { + Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness=" + + wakefulnessToString(mWakefulness) + + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout)); + } } } - private void updateNativePowerStateLocked() { - if (!mHeadless) { - nativeSetPowerState( - (mPowerState & SCREEN_ON_BIT) != 0, - (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT); + /** + * Called when a user activity timeout has occurred. + * Simply indicates that something about user activity has changed so that the new + * state can be recomputed when the power state is updated. + * + * This function must have no other side-effects besides setting the dirty + * bit and calling update power state. Wakefulness transitions are handled elsewhere. + */ + private void handleUserActivityTimeout() { // runs on handler thread + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "handleUserActivityTimeout"); + } + + mDirty |= DIRTY_USER_ACTIVITY; + updatePowerStateLocked(); } } - private int screenOffFinishedAnimatingLocked(int reason) { - // I don't think we need to check the current state here because all of these - // Power.setScreenState and sendNotificationLocked can both handle being - // called multiple times in the same state. -joeo - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, 0, 0); - int err = setScreenStateLocked(false); - if (err == 0) { - mScreenOffReason = reason; - sendNotificationLocked(false, reason); + private int getScreenOffTimeoutLocked() { + int timeout = mScreenOffTimeoutSetting; + if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { + timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin); } - return err; + return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT); } - private boolean batteryIsLow() { - return (!mIsPowered && - mBatteryService.getBatteryLevel() <= LOW_BATTERY_THRESHOLD); + private int getScreenDimDurationLocked() { + return SCREEN_DIM_DURATION; } - private boolean shouldDeferScreenOnLocked() { - if (mPreparingForScreenOn) { - // Currently waiting for confirmation from the policy that it - // is okay to turn on the screen. Don't allow the screen to go - // on until that is done. - if (DEBUG_SCREEN_ON) Slog.i(TAG, - "updateLights: delaying screen on due to mPreparingForScreenOn"); - return true; - } else { - // If there is a screen-on command in the notification queue, we - // can't turn the screen on until it has been processed (and we - // have set mPreparingForScreenOn) or it has been dropped. - for (int i=0; i<mBroadcastQueue.length; i++) { - if (mBroadcastQueue[i] == 1) { - if (DEBUG_SCREEN_ON) Slog.i(TAG, - "updateLights: delaying screen on due to notification queue"); - return true; + /** + * Updates the wakefulness of the device. + * + * This is the function that decides whether the device should start napping + * based on the current wake locks and user activity state. It may modify mDirty + * if the wakefulness changes. + * + * Returns true if the wakefulness changed and we need to restart power state calculation. + */ + private boolean updateWakefulnessLocked(int dirty) { + boolean changed = false; + if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED + | DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) { + if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakefulnessLocked: Nap time..."); } + mWakefulness = WAKEFULNESS_NAPPING; + mDirty |= DIRTY_WAKEFULNESS; + changed = true; } } - return false; + return changed; } - private void updateLightsLocked(int newState, int forceState) { - final int oldState = mPowerState; + // Also used when exiting a dream to determine whether we should go back + // to being fully awake or else go to sleep for good. + private boolean isItBedTimeYetLocked() { + return mBootCompleted && !mStayOn + && (mWakeLockSummary + & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0 + && (mUserActivitySummary + & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0; + } - // If the screen is not currently on, we will want to delay actually - // turning the lights on if we are still getting the UI put up. - if ((oldState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) { - // Don't turn screen on until we know we are really ready to. - // This is to avoid letting the screen go on before things like the - // lock screen have been displayed. - if ((mSkippedScreenOn = shouldDeferScreenOnLocked())) { - newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT); - } + /** + * Determines whether to post a message to the sandman to update the dream state. + */ + private void updateDreamLocked(int dirty) { + if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS + | DIRTY_IS_POWERED | DIRTY_STAY_ON)) != 0) { + scheduleSandmanLocked(); } + } - if ((newState & SCREEN_ON_BIT) != 0) { - // Only turn on the buttons or keyboard if the screen is also on. - // We should never see the buttons on but not the screen. - newState = applyButtonState(newState); - newState = applyKeyboardState(newState); - } - final int realDifference = (newState ^ oldState); - final int difference = realDifference | forceState; - if (difference == 0) { - return; + private void scheduleSandmanLocked() { + if (!mSandmanScheduled) { + mSandmanScheduled = true; + Message msg = mHandler.obtainMessage(MSG_SANDMAN); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); } + } - int offMask = 0; - int dimMask = 0; - int onMask = 0; + /** + * Called when the device enters or exits a napping or dreaming state. + * + * We do this asynchronously because we must call out of the power manager to start + * the dream and we don't want to hold our lock while doing so. There is a risk that + * the device will wake or go to sleep in the meantime so we have to handle that case. + */ + private void handleSandman() { // runs on handler thread + // Handle preconditions. + boolean startDreaming = false; + synchronized (mLock) { + mSandmanScheduled = false; - int preferredBrightness = getPreferredBrightness(); + if (DEBUG_SPEW) { + Log.d(TAG, "handleSandman: canDream=" + canDreamLocked() + + ", mWakefulness=" + wakefulnessToString(mWakefulness)); + } - if ((difference & KEYBOARD_BRIGHT_BIT) != 0) { - if ((newState & KEYBOARD_BRIGHT_BIT) == 0) { - offMask |= KEYBOARD_BRIGHT_BIT; - } else { - onMask |= KEYBOARD_BRIGHT_BIT; + if (canDreamLocked() && mWakefulness == WAKEFULNESS_NAPPING) { + startDreaming = true; } } - if ((difference & BUTTON_BRIGHT_BIT) != 0) { - if ((newState & BUTTON_BRIGHT_BIT) == 0) { - offMask |= BUTTON_BRIGHT_BIT; - } else { - onMask |= BUTTON_BRIGHT_BIT; + // Get the dream manager, if needed. + if (startDreaming && mDreamManager == null) { + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.checkService("dreams")); + if (mDreamManager == null) { + Slog.w(TAG, "Unable to find IDreamManager."); } } - if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { - int nominalCurrentValue = -1; - // If there was an actual difference in the light state, then - // figure out the "ideal" current value based on the previous - // state. Otherwise, this is a change due to the brightness - // override, so we want to animate from whatever the current - // value is. - if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { - switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) { - case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT: - nominalCurrentValue = preferredBrightness; - break; - case SCREEN_ON_BIT: - nominalCurrentValue = mScreenBrightnessDim; - break; - case 0: - nominalCurrentValue = PowerManager.BRIGHTNESS_OFF; - break; - case SCREEN_BRIGHT_BIT: - default: - // not possible - nominalCurrentValue = (int)mScreenBrightnessAnimator.getCurrentBrightness(); - break; + // Start dreaming if needed. + // We only control the dream on the handler thread, so we don't need to worry about + // concurrent attempts to start or stop the dream. + boolean isDreaming = false; + if (mDreamManager != null) { + try { + isDreaming = mDreamManager.isDreaming(); + if (startDreaming && !isDreaming) { + Slog.i(TAG, "Entering dreamland."); + mDreamManager.dream(); + isDreaming = mDreamManager.isDreaming(); + if (!isDreaming) { + Slog.i(TAG, "Could not enter dreamland. Sleep will be dreamless."); + } } + } catch (RemoteException ex) { } - int brightness = preferredBrightness; - int steps = ANIM_STEPS; - if ((newState & SCREEN_BRIGHT_BIT) == 0) { - // dim or turn off backlight, depending on if the screen is on - // the scale is because the brightness ramp isn't linear and this biases - // it so the later parts take longer. - final float scale = 1.5f; - float ratio = (((float)mScreenBrightnessDim)/preferredBrightness); - if (ratio > 1.0f) ratio = 1.0f; - if ((newState & SCREEN_ON_BIT) == 0) { - if ((oldState & SCREEN_BRIGHT_BIT) != 0) { - // was bright - steps = ANIM_STEPS; - } else { - // was dim - steps = (int)(ANIM_STEPS*ratio*scale); - } - brightness = PowerManager.BRIGHTNESS_OFF; - } else { - if ((oldState & SCREEN_ON_BIT) != 0) { - // was bright - steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale); - } else { - // was dim - steps = (int)(ANIM_STEPS*ratio); - } - final int stayOnConditions = getStayOnConditionsLocked(); - if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) { - // If the "stay on while plugged in" option is - // turned on, then the screen will often not - // automatically turn off while plugged in. To - // still have a sense of when it is inactive, we - // will then count going dim as turning off. - mScreenOffTime = SystemClock.elapsedRealtime(); - } - brightness = mScreenBrightnessDim; + } + + // Update dream state. + // We might need to stop the dream again if the preconditions changed. + boolean continueDreaming = false; + synchronized (mLock) { + if (isDreaming && canDreamLocked()) { + if (mWakefulness == WAKEFULNESS_NAPPING) { + mWakefulness = WAKEFULNESS_DREAMING; + mDirty |= DIRTY_WAKEFULNESS; + updatePowerStateLocked(); + continueDreaming = true; + } else if (mWakefulness == WAKEFULNESS_DREAMING) { + continueDreaming = true; } } - if (mWaitingForFirstLightSensor && (newState & SCREEN_ON_BIT) != 0) { - steps = IMMEDIATE_ANIM_STEPS; + if (!continueDreaming) { + handleDreamFinishedLocked(); } - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(brightness); - } catch (RemoteException e) { - // Nothing interesting to do. - } finally { - Binder.restoreCallingIdentity(identity); - } - if (!mSkippedScreenOn) { - int dt = steps * NOMINAL_FRAME_TIME_MS; - mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, dt); - if (DEBUG_SCREEN_ON) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Setting screen brightness: " + brightness, e); + // Allow the sandman to detect when the dream has ended. + // FIXME: The DreamManagerService should tell us explicitly. + if (mWakefulness == WAKEFULNESS_DREAMING + || mWakefulness == WAKEFULNESS_NAPPING) { + if (!mSandmanScheduled) { + mSandmanScheduled = true; + Message msg = mHandler.obtainMessage(MSG_SANDMAN); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, 1000); } } } - if (DEBUG) { - Slog.d(TAG, "offMask=0x" + Integer.toHexString(offMask) - + " dimMask=0x" + Integer.toHexString(dimMask) - + " onMask=0x" + Integer.toHexString(onMask) - + " difference=0x" + Integer.toHexString(difference) - + " realDifference=0x" + Integer.toHexString(realDifference) - + " forceState=0x" + Integer.toHexString(forceState) - ); - } - - if (offMask != 0) { - if (DEBUG) Slog.i(TAG, "Setting brightess off: " + offMask); - setLightBrightness(offMask, PowerManager.BRIGHTNESS_OFF); - } - if (dimMask != 0) { - int brightness = mScreenBrightnessDim; - if ((newState & BATTERY_LOW_BIT) != 0 && - brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) { - brightness = PowerManager.BRIGHTNESS_LOW_BATTERY; - } - if (DEBUG) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask); - setLightBrightness(dimMask, brightness); - } - if (onMask != 0) { - int brightness = getPreferredBrightness(); - if ((newState & BATTERY_LOW_BIT) != 0 && - brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) { - brightness = PowerManager.BRIGHTNESS_LOW_BATTERY; + // Stop dreaming if needed. + // It's possible that something else changed to make us need to start the dream again. + // If so, then the power manager will have posted another message to the handler + // to take care of it later. + if (mDreamManager != null) { + try { + if (!continueDreaming && isDreaming) { + Slog.i(TAG, "Leaving dreamland."); + mDreamManager.awaken(); + } + } catch (RemoteException ex) { } - if (DEBUG) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask); - setLightBrightness(onMask, brightness); } } /** - * Note: by design this class does not hold mLocks while calling native methods. - * Nor should it. Ever. + * Returns true if the device is allowed to dream in its current state, + * assuming there has been no recent user activity and no wake locks are held. */ - class ScreenBrightnessAnimator extends HandlerThread { - static final int ANIMATE_LIGHTS = 10; - static final int ANIMATE_POWER_OFF = 11; - volatile int startValue; - volatile int endValue; - volatile int startSensorValue; - volatile int endSensorValue; - volatile int currentValue; - private int currentMask; - private int duration; - private long startTimeMillis; - private final String prefix; - - public ScreenBrightnessAnimator(String name, int priority) { - super(name, priority); - prefix = name; - } - - @Override - protected void onLooperPrepared() { - mScreenBrightnessHandler = new Handler() { - public void handleMessage(Message msg) { - int brightnessMode = (mAutoBrightessEnabled && !mInitialAnimation - ? LightsService.BRIGHTNESS_MODE_SENSOR - : LightsService.BRIGHTNESS_MODE_USER); - if (msg.what == ANIMATE_LIGHTS) { - final int mask = msg.arg1; - int value = msg.arg2; - long tStart = SystemClock.uptimeMillis(); - if ((mask & SCREEN_BRIGHT_BIT) != 0) { - if (DEBUG_LIGHT_ANIMATION) Slog.v(TAG, "Set brightness: " + value); - mLcdLight.setBrightness(value, brightnessMode); - } - long elapsed = SystemClock.uptimeMillis() - tStart; - if ((mask & BUTTON_BRIGHT_BIT) != 0) { - mButtonLight.setBrightness(value); - } - if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { - mKeyboardLight.setBrightness(value); - } - - if (elapsed > 100) { - Slog.e(TAG, "Excessive delay setting brightness: " + elapsed - + "ms, mask=" + mask); - } - - // Throttle brightness updates to frame refresh rate - int delay = elapsed < NOMINAL_FRAME_TIME_MS ? NOMINAL_FRAME_TIME_MS : 1; - synchronized(this) { - currentValue = value; - } - animateInternal(mask, false, delay); - } else if (msg.what == ANIMATE_POWER_OFF) { - int mode = msg.arg1; - nativeStartSurfaceFlingerAnimation(mode); - } - } - }; - synchronized (this) { - mInitComplete = true; - notifyAll(); - } - } - - private void animateInternal(int mask, boolean turningOff, int delay) { - synchronized (this) { - if (currentValue != endValue) { - final long now = SystemClock.elapsedRealtime(); - final int elapsed = (int) (now - startTimeMillis); - int newValue; - if (elapsed < duration) { - int delta = endValue - startValue; - newValue = startValue + delta * elapsed / duration; - newValue = Math.max(PowerManager.BRIGHTNESS_OFF, newValue); - newValue = Math.min(PowerManager.BRIGHTNESS_ON, newValue); - // Optimization to delay next step until a change will occur. - if (delay > 0 && newValue == currentValue) { - final int timePerStep = duration / Math.abs(delta); - delay = Math.min(duration - elapsed, timePerStep); - newValue += delta < 0 ? -1 : 1; - } - // adjust the peak sensor value until we get to the target sensor value - delta = endSensorValue - startSensorValue; - mHighestLightSensorValue = startSensorValue + delta * elapsed / duration; - } else { - newValue = endValue; - mHighestLightSensorValue = endSensorValue; - if (endValue > 0) { - mInitialAnimation = false; - } - } - - if (DEBUG_LIGHT_ANIMATION) { - Slog.v(TAG, "Animating light: " + "start:" + startValue - + ", end:" + endValue + ", elapsed:" + elapsed - + ", duration:" + duration + ", current:" + currentValue - + ", newValue:" + newValue - + ", delay:" + delay - + ", highestSensor:" + mHighestLightSensorValue); - } + private boolean canDreamLocked() { + return mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting; + } - if (turningOff && !mHeadless && !mAnimateScreenLights) { - int mode = mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR - ? 0 : mAnimationSetting; - if (DEBUG_LIGHT_ANIMATION) { - Slog.v(TAG, "Doing power-off anim, mode=" + mode); - } - mScreenBrightnessHandler.obtainMessage(ANIMATE_POWER_OFF, mode, 0) - .sendToTarget(); - } - mScreenBrightnessHandler.removeMessages( - ScreenBrightnessAnimator.ANIMATE_LIGHTS); - Message msg = mScreenBrightnessHandler - .obtainMessage(ANIMATE_LIGHTS, mask, newValue); - mScreenBrightnessHandler.sendMessageDelayed(msg, delay); - } + /** + * Called when a dream is ending to figure out what to do next. + */ + private void handleDreamFinishedLocked() { + if (mWakefulness == WAKEFULNESS_NAPPING + || mWakefulness == WAKEFULNESS_DREAMING) { + if (isItBedTimeYetLocked()) { + goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + updatePowerStateLocked(); + } else { + wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); + updatePowerStateLocked(); } } + } - public void dump(PrintWriter pw, String string) { - pw.println(string); - pw.println(" animating: " + "start:" + startValue + ", end:" + endValue - + ", duration:" + duration + ", current:" + currentValue); - pw.println(" startSensorValue:" + startSensorValue - + " endSensorValue:" + endSensorValue); - pw.println(" startTimeMillis:" + startTimeMillis - + " now:" + SystemClock.elapsedRealtime()); - pw.println(" currentMask:" + dumpPowerState(currentMask)); - } - - public void animateTo(int target, int mask, int animationDuration) { - animateTo(target, mHighestLightSensorValue, mask, animationDuration); - } - public void animateTo(int target, int sensorTarget, int mask, int animationDuration) { - synchronized(this) { - if ((mask & SCREEN_BRIGHT_BIT) == 0) { - // We only animate keyboard and button when passed in with SCREEN_BRIGHT_BIT. - if ((mask & BUTTON_BRIGHT_BIT) != 0) { - mButtonLight.setBrightness(target); - } - if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { - mKeyboardLight.setBrightness(target); - } - return; - } - if (isAnimating() && (mask ^ currentMask) != 0) { - // current animation is unrelated to new animation, jump to final values - cancelAnimation(); - } - if (mInitialAnimation) { - // jump to final value in one step the first time the brightness is set - animationDuration = 0; - if (target > 0) { - mInitialAnimation = false; - } - } - startValue = currentValue; - endValue = target; - startSensorValue = mHighestLightSensorValue; - endSensorValue = sensorTarget; - currentMask = mask; - duration = (int) (mWindowScaleAnimation * animationDuration); - startTimeMillis = SystemClock.elapsedRealtime(); - - if (DEBUG_LIGHT_ANIMATION) { - Slog.v(TAG, "animateTo(target=" + target - + ", sensor=" + sensorTarget - + ", mask=" + mask - + ", duration=" + animationDuration +")" - + ", currentValue=" + currentValue - + ", startTime=" + startTimeMillis); + /** + * Updates the display power state asynchronously. + * When the update is finished, mDisplayReady will be set to true. The display + * controller posts a message to tell us when the actual display power state + * has been updated so we come back here to double-check and finish up. + * + * This function recalculates the display power state each time. + */ + private void updateDisplayPowerStateLocked(int dirty) { + if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS + | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED + | DIRTY_SETTINGS)) != 0) { + int newScreenState = getDesiredScreenPowerState(); + if (newScreenState != mDisplayPowerRequest.screenState) { + if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF + && mDisplayPowerRequest.screenState + != DisplayPowerRequest.SCREEN_STATE_OFF) { + mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime(); } - if (target != currentValue) { - final boolean doScreenAnim = (mask & (SCREEN_BRIGHT_BIT | SCREEN_ON_BIT)) != 0; - final boolean turningOff = endValue == PowerManager.BRIGHTNESS_OFF; - if (turningOff && doScreenAnim) { - // Cancel all pending animations since we're turning off - mScreenBrightnessHandler.removeCallbacksAndMessages(null); - screenOffFinishedAnimatingLocked(mScreenOffReason); - duration = 200; // TODO: how long should this be? - } - if (doScreenAnim) { - animateInternal(mask, turningOff, 0); - } - // TODO: Handle keyboard light animation when we have devices that support it - } + mDisplayPowerRequest.screenState = newScreenState; + nativeSetPowerState( + newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF, + newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT); } - } - public int getCurrentBrightness() { - synchronized (this) { - return currentValue; + int screenBrightness = mScreenBrightnessSettingDefault; + boolean autoBrightness = (mScreenBrightnessModeSetting == + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { + screenBrightness = mScreenBrightnessOverrideFromWindowManager; + autoBrightness = false; + } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) { + screenBrightness = mTemporaryScreenBrightnessSettingOverride; + } else if (isValidBrightness(mScreenBrightnessSetting)) { + screenBrightness = mScreenBrightnessSetting; } - } - - public boolean isAnimating() { - synchronized (this) { - return currentValue != endValue; + if (autoBrightness) { + screenBrightness = mScreenBrightnessSettingDefault; } - } + screenBrightness = Math.max(Math.min(screenBrightness, + mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum); + mDisplayPowerRequest.screenBrightness = screenBrightness; + mDisplayPowerRequest.useAutoBrightness = autoBrightness; - public void cancelAnimation() { - animateTo(endValue, currentMask, 0); - } - } + mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); - private void setLightBrightness(int mask, int value) { - mScreenBrightnessAnimator.animateTo(value, mask, 0); - } + mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest, + mRequestWaitForNegativeProximity); + mRequestWaitForNegativeProximity = false; - private int getPreferredBrightness() { - int brightness = mScreenBrightnessSetting; - if (mScreenBrightnessOverride >= 0) { - brightness = mScreenBrightnessOverride; - } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness - && mAutoBrightessEnabled) { - brightness = mLightSensorScreenBrightness; - } - // Don't let applications turn the screen all the way off - return Math.max(brightness, mScreenBrightnessDim); - } - - private int applyButtonState(int state) { - int brightness = -1; - if ((state & BATTERY_LOW_BIT) != 0) { - // do not override brightness if the battery is low - return state; - } - if (mButtonBrightnessOverride >= 0) { - brightness = mButtonBrightnessOverride; - } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) { - brightness = mLightSensorButtonBrightness; - } - if (brightness > 0) { - return state | BUTTON_BRIGHT_BIT; - } else if (brightness == 0) { - return state & ~BUTTON_BRIGHT_BIT; - } else { - return state; + if (DEBUG_SPEW) { + Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady + + ", newScreenState=" + newScreenState + + ", mWakefulness=" + mWakefulness + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + + ", mBootCompleted=" + mBootCompleted); + } } } - private int applyKeyboardState(int state) { - int brightness = -1; - if ((state & BATTERY_LOW_BIT) != 0) { - // do not override brightness if the battery is low - return state; - } - if (!mKeyboardVisible) { - brightness = 0; - } else if (mButtonBrightnessOverride >= 0) { - brightness = mButtonBrightnessOverride; - } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) { - brightness = mLightSensorKeyboardBrightness; - } - if (brightness > 0) { - return state | KEYBOARD_BRIGHT_BIT; - } else if (brightness == 0) { - return state & ~KEYBOARD_BRIGHT_BIT; - } else { - return state; - } + private static boolean isValidBrightness(int value) { + return value >= 0 && value <= 255; } - public boolean isScreenOn() { - synchronized (mLocks) { - return (mPowerState & SCREEN_ON_BIT) != 0; + private int getDesiredScreenPowerState() { + if (mWakefulness == WAKEFULNESS_ASLEEP) { + return DisplayPowerRequest.SCREEN_STATE_OFF; } - } - boolean isScreenBright() { - synchronized (mLocks) { - return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT; + if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 + || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 + || !mBootCompleted) { + return DisplayPowerRequest.SCREEN_STATE_BRIGHT; } - } - private boolean isScreenTurningOffLocked() { - return (mScreenBrightnessAnimator.isAnimating() - && mScreenBrightnessAnimator.endValue == PowerManager.BRIGHTNESS_OFF - && (mScreenBrightnessAnimator.currentMask & SCREEN_BRIGHT_BIT) != 0); + return DisplayPowerRequest.SCREEN_STATE_DIM; } - private boolean shouldLog(long time) { - synchronized (mLocks) { - if (time > (mWarningSpewThrottleTime + (60*60*1000))) { - mWarningSpewThrottleTime = time; - mWarningSpewThrottleCount = 0; - return true; - } else if (mWarningSpewThrottleCount < 30) { - mWarningSpewThrottleCount++; - return true; - } else { - return false; - } - } - } - - private void forceUserActivityLocked() { - if (isScreenTurningOffLocked()) { - // cancel animation so userActivity will succeed - mScreenBrightnessAnimator.cancelAnimation(); + private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks = + new DisplayPowerController.Callbacks() { + @Override + public void onStateChanged() { + mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED; + updatePowerStateLocked(); } - boolean savedActivityAllowed = mUserActivityAllowed; - mUserActivityAllowed = true; - userActivity(SystemClock.uptimeMillis(), false); - mUserActivityAllowed = savedActivityAllowed; - } - - public void userActivityWithForce(long time, boolean noChangeLights, boolean force) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - userActivity(time, -1, noChangeLights, PowerManager.USER_ACTIVITY_EVENT_OTHER, force, false); - } - public void userActivity(long time, boolean noChangeLights) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) - != PackageManager.PERMISSION_GRANTED) { - if (shouldLog(time)) { - Slog.w(TAG, "Caller does not have DEVICE_POWER permission. pid=" - + Binder.getCallingPid() + " uid=" + Binder.getCallingUid()); - } - return; + @Override + public void onProximityNegative() { + userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); } + }; - userActivity(time, -1, noChangeLights, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, false); - } - - public void userActivity(long time, boolean noChangeLights, int eventType) { - userActivity(time, -1, noChangeLights, eventType, false, false); - } - - public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) { - userActivity(time, -1, noChangeLights, eventType, force, false); + private boolean shouldUseProximitySensorLocked() { + return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0; } - /* - * Reset the user activity timeout to now + timeout. This overrides whatever else is going - * on with user activity. Don't use this function. + /** + * Updates the suspend blocker that keeps the CPU alive. + * + * This function must have no other side-effects. */ - public void clearUserActivityTimeout(long now, long timeout) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now"); - userActivity(now, timeout, false, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, false); - } - - private void userActivity(long time, long timeoutOverride, boolean noChangeLights, - int eventType, boolean force, boolean ignoreIfScreenOff) { - - if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == PowerManager.USER_ACTIVITY_EVENT_TOUCH)) { - if (false) { - Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey)); - } - return; - } - - synchronized (mLocks) { - if (DEBUG) { - Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time - + " mUserActivityAllowed=" + mUserActivityAllowed - + " mUserState=0x" + Integer.toHexString(mUserState) - + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState) - + " mProximitySensorActive=" + mProximitySensorActive - + " timeoutOverride=" + timeoutOverride - + " force=" + force); - } - // ignore user activity if we are in the process of turning off the screen - if (isScreenTurningOffLocked()) { - Slog.d(TAG, "ignoring user activity while turning off screen"); - return; - } - // ignore if the caller doesn't want this to allow the screen to turn - // on, and the screen is currently off. - if (ignoreIfScreenOff && (mPowerState & SCREEN_ON_BIT) == 0) { - return; - } - // Disable proximity sensor if if user presses power key while we are in the - // "waiting for proximity sensor to go negative" state. - if (mProximitySensorActive && mProximityWakeLockCount == 0) { - mProximitySensorActive = false; - } - if (mLastEventTime <= time || force) { - mLastEventTime = time; - if ((mUserActivityAllowed && !mProximitySensorActive) || force) { - // Only turn on button backlights if a button was pressed - // and auto brightness is disabled - if (eventType == PowerManager.USER_ACTIVITY_EVENT_BUTTON && !mUseSoftwareAutoBrightness) { - mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT); - } else { - // don't clear button/keyboard backlights when the screen is touched. - mUserState |= SCREEN_BRIGHT; - } - - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteUserActivity(uid, eventType); - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(ident); - } - - mWakeLockState = mLocks.reactivateScreenLocksLocked(); - setPowerState(mUserState | mWakeLockState, noChangeLights, - WindowManagerPolicy.OFF_BECAUSE_OF_USER); - setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT); + private void updateSuspendBlockerLocked() { + boolean wantCpu = isCpuNeededLocked(); + if (wantCpu != mHoldingWakeLockSuspendBlocker) { + mHoldingWakeLockSuspendBlocker = wantCpu; + if (wantCpu) { + if (DEBUG) { + Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker."); } + mWakeLockSuspendBlocker.acquire(); + } else { + if (DEBUG) { + Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker."); + } + mWakeLockSuspendBlocker.release(); } } + } - if (mPolicy != null) { - mPolicy.userActivity(); - } + private boolean isCpuNeededLocked() { + return !mBootCompleted + || mWakeLockSummary != 0 + || mUserActivitySummary != 0 + || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF + || !mDisplayReady; } - private int getAutoBrightnessValue(int sensorValue, int[] values) { + @Override // Binder call + public boolean isScreenOn() { + final long ident = Binder.clearCallingIdentity(); try { - int i; - for (i = 0; i < mAutoBrightnessLevels.length; i++) { - if (sensorValue < mAutoBrightnessLevels[i]) { - break; - } - } - // This is the range of brightness values that we can use. - final int minval = values[0]; - final int maxval = values[mAutoBrightnessLevels.length]; - // This is the range we will be scaling. We put some padding - // at the low and high end to give the adjustment a little better - // impact on the actual observed value. - final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION; - // This is the desired brightness value from 0.0 to 1.0. - float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range); - // Apply a scaling to the value based on the adjustment. - if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) { - float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting); - if (adj <= .00001) { - valf = 1; - } else { - valf /= adj; - } - } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) { - float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting); - valf *= adj; - } - // Apply an additional offset to the value based on the adjustment. - valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE; - // Convert the 0.0-1.0 value back to a brightness integer. - int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2); - if (val < minval) val = minval; - else if (val > maxval) val = maxval; - return val; - } catch (Exception e) { - // guard against null pointer or index out of bounds errors - Slog.e(TAG, "Values array must be non-empty and must be one element longer than " - + "the auto-brightness levels array. Check config.xml.", e); - return 255; - } - } - - private Runnable mProximityTask = new Runnable() { - public void run() { - synchronized (mLocks) { - if (mProximityPendingValue != -1) { - proximityChangedLocked(mProximityPendingValue == 1); - mProximityPendingValue = -1; - } - if (mProximityPartialLock.isHeld()) { - mProximityPartialLock.release(); - } - } - } - }; - - private Runnable mAutoBrightnessTask = new Runnable() { - public void run() { - synchronized (mLocks) { - if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) { - int value = (int)mLightSensorPendingValue; - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - lightSensorChangedLocked(value, false); - } - } - } - }; - - /** used to prevent lightsensor changes while turning on. */ - private boolean mInitialAnimation = true; - - private void dockStateChanged(int state) { - synchronized (mLocks) { - mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED); - if (mIsDocked) { - // allow brightness to decrease when docked - mHighestLightSensorValue = -1; - } - if ((mPowerState & SCREEN_ON_BIT) != 0) { - // force lights recalculation - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - lightSensorChangedLocked(value, false); - } + return isScreenOnInternal(); + } finally { + Binder.restoreCallingIdentity(ident); } } - private void lightSensorChangedLocked(int value, boolean immediate) { - if (DEBUG_LIGHT_SENSOR) { - Slog.d(TAG, "lightSensorChangedLocked value=" + value + " immediate=" + immediate); + private boolean isScreenOnInternal() { + synchronized (mLock) { + return !mSystemReady + || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF; } + } - // Don't do anything if the screen is off. - if ((mPowerState & SCREEN_ON_BIT) == 0) { - if (DEBUG_LIGHT_SENSOR) { - Slog.d(TAG, "dropping lightSensorChangedLocked because screen is off"); - } - return; - } - - if (mLightSensorValue != value) { - mLightSensorValue = value; - if ((mPowerState & BATTERY_LOW_BIT) == 0) { - // use maximum light sensor value seen since screen went on for LCD to avoid flicker - // we only do this if we are undocked, since lighting should be stable when - // stationary in a dock. - int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues); - int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues); - int keyboardValue; - if (mKeyboardVisible) { - keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues); - } else { - keyboardValue = 0; - } - mLightSensorScreenBrightness = lcdValue; - mLightSensorButtonBrightness = buttonValue; - mLightSensorKeyboardBrightness = keyboardValue; - - if (DEBUG_LIGHT_SENSOR) { - Slog.d(TAG, "lcdValue " + lcdValue); - Slog.d(TAG, "buttonValue " + buttonValue); - Slog.d(TAG, "keyboardValue " + keyboardValue); - } - - if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) { - if (!mSkippedScreenOn && !mInitialAnimation) { - final int steps; - if (immediate) { - steps = IMMEDIATE_ANIM_STEPS; - } else { - synchronized (mScreenBrightnessAnimator) { - if (mScreenBrightnessAnimator.currentValue <= lcdValue) { - steps = AUTOBRIGHTNESS_ANIM_STEPS; - } else { - steps = AUTODIMNESS_ANIM_STEPS; - } - } - } - mScreenBrightnessAnimator.animateTo(lcdValue, value, - SCREEN_BRIGHT_BIT, steps * NOMINAL_FRAME_TIME_MS); - } - } - if (mButtonBrightnessOverride < 0) { - mButtonLight.setBrightness(buttonValue); - } - if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) { - mKeyboardLight.setBrightness(keyboardValue); - } - } - } + private void handleBatteryStateChangedLocked() { + mDirty |= DIRTY_BATTERY_STATE; + updatePowerStateLocked(); } - /** - * The user requested that we go to sleep (probably with the power button). - * This overrides all wake locks that are held. - */ - public void goToSleep(long time) - { - goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER); + private void handleBootCompletedLocked() { + final long now = SystemClock.uptimeMillis(); + mBootCompleted = true; + mDirty |= DIRTY_BOOT_COMPLETED; + userActivityNoUpdateLocked( + now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); } - /** - * The user requested that we go to sleep (probably with the power button). - * This overrides all wake locks that are held. - */ - public void goToSleepWithReason(long time, int reason) - { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - synchronized (mLocks) { - goToSleepLocked(time, reason); - } + private void handleDockStateChangedLocked(int dockState) { + // TODO } /** * Reboot the device immediately, passing 'reason' (may be null) * to the underlying __reboot system call. Should not return. */ - public void reboot(String reason) - { + @Override // Binder call + public void reboot(String reason) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); - if (mHandler == null || !ActivityManagerNative.isSystemReady()) { + final long ident = Binder.clearCallingIdentity(); + try { + rebootInternal(reason); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void rebootInternal(final String reason) { + if (mHandler == null || !mSystemReady) { throw new IllegalStateException("Too early to call reboot()"); } - final String finalReason = reason; Runnable runnable = new Runnable() { public void run() { synchronized (this) { - ShutdownThread.reboot(mContext, finalReason, false); + ShutdownThread.reboot(mContext, reason, false); } - } }; + // ShutdownThread must run on a looper capable of displaying the UI. - mHandler.post(runnable); + Message msg = Message.obtain(mHandler, runnable); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); // PowerManager.reboot() is documented not to return so just wait for the inevitable. synchronized (runnable) { @@ -2798,11 +1463,23 @@ public class PowerManagerService extends IPowerManager.Stub * Crash the runtime (causing a complete restart of the Android framework). * Requires REBOOT permission. Mostly for testing. Should not return. */ - public void crash(final String message) - { + @Override // Binder call + public void crash(String message) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + + final long ident = Binder.clearCallingIdentity(); + try { + crashInternal(message); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void crashInternal(final String message) { Thread t = new Thread("PowerManagerService.crash()") { - public void run() { throw new RuntimeException(message); } + public void run() { + throw new RuntimeException(message); + } }; try { t.start(); @@ -2812,581 +1489,554 @@ public class PowerManagerService extends IPowerManager.Stub } } - private void goToSleepLocked(long time, int reason) { - if (DEBUG) { - Exception ex = new Exception(); - ex.fillInStackTrace(); - Slog.d(TAG, "goToSleep mLastEventTime=" + mLastEventTime + " time=" + time - + " reason=" + reason, ex); - } - - if (mLastEventTime <= time) { - mLastEventTime = time; - // cancel all of the wake locks - mWakeLockState = SCREEN_OFF; - int N = mLocks.size(); - int numCleared = 0; - boolean proxLock = false; - for (int i=0; i<N; i++) { - WakeLock wl = mLocks.get(i); - if (isScreenLock(wl.flags)) { - if (((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) - && reason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) { - proxLock = true; - } else { - mLocks.get(i).activated = false; - numCleared++; - } - } - } - if (!proxLock) { - mProxIgnoredBecauseScreenTurnedOff = true; - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "setting mProxIgnoredBecauseScreenTurnedOff"); - } - } - EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared); - mStillNeedSleepNotification = true; - mUserState = SCREEN_OFF; - setPowerState(SCREEN_OFF, false, reason); - cancelTimerLocked(); + @Override // Binder call + public void clearUserActivityTimeout(long now, long timeout) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + + @Override // Binder call + public void setPokeLock(int pokey, IBinder lock, String tag) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + + /** + * Set the setting that determines whether the device stays on when plugged in. + * The argument is a bit string, with each bit specifying a power source that, + * when the device is connected to that source, causes the device to stay on. + * See {@link android.os.BatteryManager} for the list of power sources that + * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC} + * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB} + * + * Used by "adb shell svc power stayon ..." + * + * @param val an {@code int} containing the bits that specify which power sources + * should cause the device to stay on. + */ + @Override // Binder call + public void setStayOnSetting(int val) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setStayOnSettingInternal(val); + } finally { + Binder.restoreCallingIdentity(ident); } } - public long timeSinceScreenOn() { - synchronized (mLocks) { - if ((mPowerState & SCREEN_ON_BIT) != 0) { - return 0; - } - return SystemClock.elapsedRealtime() - mScreenOffTime; + private void setStayOnSettingInternal(int val) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); + } + + /** + * Used by device administration to set the maximum screen off timeout. + * + * This method must only be called by the device administration policy manager. + */ + @Override // Binder call + public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) { + final long ident = Binder.clearCallingIdentity(); + try { + setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs); + } finally { + Binder.restoreCallingIdentity(ident); } } - public void setKeyboardVisibility(boolean visible) { - synchronized (mLocks) { - if (DEBUG) { - Slog.d(TAG, "setKeyboardVisibility: " + visible); - } - if (mKeyboardVisible != visible) { - mKeyboardVisible = visible; - // don't signal user activity if the screen is off; other code - // will take care of turning on due to a true change to the lid - // switch and synchronized with the lock screen. - if ((mPowerState & SCREEN_ON_BIT) != 0) { - if (mUseSoftwareAutoBrightness) { - // force recompute of backlight values - if (mLightSensorValue >= 0) { - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - lightSensorChangedLocked(value, false); - } - } - userActivity(SystemClock.uptimeMillis(), false, PowerManager.USER_ACTIVITY_EVENT_BUTTON, true); - } - } + private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { + synchronized (mLock) { + mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); } } + private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() { + return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0 + && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE; + } + + @Override // Binder call + public void preventScreenOn(boolean prevent) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + /** - * When the keyguard is up, it manages the power state, and userActivity doesn't do anything. - * When disabling user activity we also reset user power state so the keyguard can reset its - * short screen timeout when keyguard is unhidden. + * Used by the phone application to make the attention LED flash when ringing. */ - public void enableUserActivity(boolean enabled) { - if (DEBUG) { - Slog.d(TAG, "enableUserActivity " + enabled); - } - synchronized (mLocks) { - mUserActivityAllowed = enabled; - if (!enabled) { - // cancel timeout and clear mUserState so the keyguard can set a short timeout - setTimeoutLocked(SystemClock.uptimeMillis(), 0); - } + @Override // Binder call + public void setAttentionLight(boolean on, int color) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setAttentionLightInternal(on, color); + } finally { + Binder.restoreCallingIdentity(ident); } } - private void setScreenBrightnessMode(int mode) { - synchronized (mLocks) { - boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) { - mAutoBrightessEnabled = enabled; - // This will get us a new value - enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn()); + private void setAttentionLightInternal(boolean on, int color) { + LightsService.Light light; + synchronized (mLock) { + if (!mSystemReady) { + return; } + light = mAttentionLight; } + + // Control light outside of lock. + light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); } - /** Sets the screen off timeouts: - * mKeylightDelay - * mDimDelay - * mScreenOffDelay - * */ - private void setScreenOffTimeoutsLocked() { - if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) { - mKeylightDelay = mShortKeylightDelay; // Configurable via secure settings - mDimDelay = -1; - mScreenOffDelay = 0; - } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) { - mKeylightDelay = MEDIUM_KEYLIGHT_DELAY; - mDimDelay = -1; - mScreenOffDelay = 0; - } else { - int totalDelay = mScreenOffTimeoutSetting; - if (totalDelay > mMaximumScreenOffTimeout) { - totalDelay = mMaximumScreenOffTimeout; - } - mKeylightDelay = LONG_KEYLIGHT_DELAY; - if (totalDelay < 0) { - // negative number means stay on as long as possible. - mScreenOffDelay = mMaximumScreenOffTimeout; - } else if (mKeylightDelay < totalDelay) { - // subtract the time that the keylight delay. This will give us the - // remainder of the time that we need to sleep to get the accurate - // screen off timeout. - mScreenOffDelay = totalDelay - mKeylightDelay; - } else { - mScreenOffDelay = 0; - } - if (mDimScreen && totalDelay >= (LONG_KEYLIGHT_DELAY + LONG_DIM_TIME)) { - mDimDelay = mScreenOffDelay - LONG_DIM_TIME; - mScreenOffDelay = LONG_DIM_TIME; - } else { - mDimDelay = -1; + /** + * Used by the Watchdog. + */ + public long timeSinceScreenWasLastOn() { + synchronized (mLock) { + if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + return 0; } - } - if (DEBUG) { - Slog.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay - + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay - + " mDimScreen=" + mDimScreen); + return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime; } } /** - * Refreshes cached secure settings. Called once on startup, and - * on subsequent changes to secure settings. + * Used by the window manager to override the screen brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. */ - private void updateSettingsValues() { - mShortKeylightDelay = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.SHORT_KEYLIGHT_DELAY_MS, - SHORT_KEYLIGHT_DELAY_DEFAULT); - // Slog.i(TAG, "updateSettingsValues(): mShortKeylightDelay now " + mShortKeylightDelay); - } - - private class LockList extends ArrayList<WakeLock> - { - void addLock(WakeLock wl) - { - int index = getIndex(wl.binder); - if (index < 0) { - this.add(wl); - } + public void setScreenBrightnessOverrideFromWindowManager(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setScreenBrightnessOverrideFromWindowManagerInternal(brightness); + } finally { + Binder.restoreCallingIdentity(ident); } + } - WakeLock removeLock(IBinder binder) - { - int index = getIndex(binder); - if (index >= 0) { - return this.remove(index); - } else { - return null; + private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) { + synchronized (mLock) { + if (mScreenBrightnessOverrideFromWindowManager != brightness) { + mScreenBrightnessOverrideFromWindowManager = brightness; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); } } + } - int getIndex(IBinder binder) - { - int N = this.size(); - for (int i=0; i<N; i++) { - if (this.get(i).binder == binder) { - return i; - } - } - return -1; - } - - int gatherState() - { - int result = 0; - int N = this.size(); - for (int i=0; i<N; i++) { - WakeLock wl = this.get(i); - if (wl.activated) { - if (isScreenLock(wl.flags)) { - result |= wl.minState; - } - } - } - return result; + /** + * Used by the window manager to override the button brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. + */ + public void setButtonBrightnessOverrideFromWindowManager(int brightness) { + // Do nothing. + // Button lights are not currently supported in the new implementation. + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + } + + /** + * Used by the settings application and brightness control widgets to + * temporarily override the current screen brightness setting so that the + * user can observe the effect of an intended settings change without applying + * it immediately. + * + * The override will be canceled when the setting value is next updated. + * + * @param brightness The overridden brightness. + * + * @see Settings.System#SCREEN_BRIGHTNESS + */ + @Override // Binder call + public void setTemporaryScreenBrightnessSettingOverride(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setTemporaryScreenBrightnessSettingOverrideInternal(brightness); + } finally { + Binder.restoreCallingIdentity(ident); } + } - int reactivateScreenLocksLocked() - { - int result = 0; - int N = this.size(); - for (int i=0; i<N; i++) { - WakeLock wl = this.get(i); - if (isScreenLock(wl.flags)) { - wl.activated = true; - result |= wl.minState; - } - } - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "reactivateScreenLocksLocked mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); + private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) { + synchronized (mLock) { + if (mTemporaryScreenBrightnessSettingOverride != brightness) { + mTemporaryScreenBrightnessSettingOverride = brightness; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); } - mProxIgnoredBecauseScreenTurnedOff = false; - return result; } } - public void setPolicy(WindowManagerPolicy p) { - synchronized (mLocks) { - mPolicy = p; - mLocks.notifyAll(); - } + /** + * Used by the settings application and brightness control widgets to + * temporarily override the current screen auto-brightness adjustment setting so that the + * user can observe the effect of an intended settings change without applying + * it immediately. + * + * The override will be canceled when the setting value is next updated. + * + * @param adj The overridden brightness, or -1 to disable the override. + * + * @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ + */ + @Override // Binder call + public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) { + // Not implemented. + // The SCREEN_AUTO_BRIGHTNESS_ADJ setting is not currently supported. + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); } - WindowManagerPolicy getPolicyLocked() { - while (mPolicy == null || !mDoneBooting) { - try { - mLocks.wait(); - } catch (InterruptedException e) { - // Ignore - } + /** + * Low-level function turn the device off immediately, without trying + * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. + */ + public static void lowLevelShutdown() { + nativeShutdown(); + } + + /** + * Low-level function to reboot the device. + * + * @param reason code to pass to the kernel (e.g. "recovery"), or null. + * @throws IOException if reboot fails for some reason (eg, lack of + * permission) + */ + public static void lowLevelReboot(String reason) throws IOException { + nativeReboot(reason); + } + + @Override // Watchdog.Monitor implementation + public void monitor() { + // Grab and release lock for watchdog monitor to detect deadlocks. + synchronized (mLock) { } - return mPolicy; } - public void systemReady() { - mSensorManager = new SystemSensorManager(mHandlerThread.getLooper()); - mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - // don't bother with the light sensor if auto brightness is handled in hardware - if (mUseSoftwareAutoBrightness) { - mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); - } - - // wait until sensors are enabled before turning on screen. - // some devices will not activate the light sensor properly on boot - // unless we do this. - if (mUseSoftwareAutoBrightness) { - // turn the screen on - setPowerState(SCREEN_BRIGHT); - } else { - // turn everything on - setPowerState(ALL_BRIGHT); + @Override // Binder call + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump PowerManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; } - synchronized (mLocks) { - Slog.d(TAG, "system ready!"); - mDoneBooting = true; + pw.println("POWER MANAGER (dumpsys power)\n"); - enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled); + final DisplayPowerController dpc; + synchronized (mLock) { + pw.println("Power Manager State:"); + pw.println(" mDirty=0x" + Integer.toHexString(mDirty)); + pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness)); + pw.println(" mIsPowered=" + mIsPowered); + pw.println(" mStayOn=" + mStayOn); + pw.println(" mBootCompleted=" + mBootCompleted); + pw.println(" mSystemReady=" + mSystemReady); + pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); + pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); + pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); + pw.println(" mSandmanScheduled=" + mSandmanScheduled); + pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); + pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); + pw.println(" mSendWakeUpFinishedNotificationWhenReady=" + + mSendWakeUpFinishedNotificationWhenReady); + pw.println(" mSendGoToSleepFinishedNotificationWhenReady=" + + mSendGoToSleepFinishedNotificationWhenReady); + pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); + pw.println(" mLastUserActivityTimeNoChangeLights=" + + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights)); + pw.println(" mDisplayReady=" + mDisplayReady); + pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(getPreferredBrightness()); - mBatteryStats.noteScreenOn(); - } catch (RemoteException e) { - // Nothing interesting to do. - } finally { - Binder.restoreCallingIdentity(identity); + pw.println(); + pw.println("Settings and Configuration:"); + pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); + pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); + pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting); + pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin=" + + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" + + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")"); + pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting); + pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting); + pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting); + pw.println(" mScreenBrightnessOverrideFromWindowManager=" + + mScreenBrightnessOverrideFromWindowManager); + pw.println(" mTemporaryScreenBrightnessSettingOverride=" + + mTemporaryScreenBrightnessSettingOverride); + pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum); + pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); + pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + + pw.println(); + pw.println("Wake Locks: size=" + mWakeLocks.size()); + for (WakeLock wl : mWakeLocks) { + pw.println(" " + wl); + } + + pw.println(); + pw.println("Suspend Blockers: size=" + mSuspendBlockers.size()); + for (SuspendBlocker sb : mSuspendBlockers) { + pw.println(" " + sb); } + + dpc = mDisplayPowerController; } - } - void bootCompleted() { - Slog.d(TAG, "bootCompleted"); - synchronized (mLocks) { - mBootCompleted = true; - userActivity(SystemClock.uptimeMillis(), false, PowerManager.USER_ACTIVITY_EVENT_BUTTON, true); - updateWakeLockLocked(); - mLocks.notifyAll(); + if (dpc != null) { + dpc.dump(pw); } } - // for watchdog - public void monitor() { - synchronized (mLocks) { } + private SuspendBlocker createSuspendBlockerLocked(String name) { + SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name); + mSuspendBlockers.add(suspendBlocker); + return suspendBlocker; } - public int getSupportedWakeLockFlags() { - int result = PowerManager.PARTIAL_WAKE_LOCK - | PowerManager.FULL_WAKE_LOCK - | PowerManager.SCREEN_DIM_WAKE_LOCK; - - if (mProximitySensor != null) { - result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; + private static String wakefulnessToString(int wakefulness) { + switch (wakefulness) { + case WAKEFULNESS_ASLEEP: + return "Asleep"; + case WAKEFULNESS_AWAKE: + return "Awake"; + case WAKEFULNESS_DREAMING: + return "Dreaming"; + case WAKEFULNESS_NAPPING: + return "Napping"; + default: + return Integer.toString(wakefulness); } + } - return result; + private static WorkSource copyWorkSource(WorkSource workSource) { + return workSource != null ? new WorkSource(workSource) : null; } - public void setBacklightBrightness(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - // Don't let applications turn the screen all the way off - synchronized (mLocks) { - brightness = Math.max(brightness, mScreenBrightnessDim); - mLcdLight.setBrightness(brightness); - mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0); - mButtonLight.setBrightness(brightness); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(brightness); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); + private final class BatteryReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + handleBatteryStateChangedLocked(); } - mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, 0); } } - public void setAutoBrightnessAdjustment(float adj) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - synchronized (mLocks) { - mLightSensorAdjustSetting = adj; - if (mSensorManager != null && mLightSensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - // force recompute of backlight values - if (mLightSensorValue >= 0) { - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - handleLightSensorValue(value, true); - } - } finally { - Binder.restoreCallingIdentity(identity); - } + private final class BootCompletedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + handleBootCompletedLocked(); } } } - public void setAttentionLight(boolean on, int color) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - mAttentionLight.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); - } - - private void enableProximityLockLocked() { - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "enableProximityLockLocked"); - } - if (!mProximitySensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - mSensorManager.registerListener(mProximityListener, mProximitySensor, - SensorManager.SENSOR_DELAY_NORMAL); - mProximitySensorEnabled = true; - } finally { - Binder.restoreCallingIdentity(identity); + private final class DockReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + handleDockStateChangedLocked(dockState); } } } - private void disableProximityLockLocked() { - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "disableProximityLockLocked"); + private final class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); } - if (mProximitySensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - mSensorManager.unregisterListener(mProximityListener); - mHandler.removeCallbacks(mProximityTask); - if (mProximityPartialLock.isHeld()) { - mProximityPartialLock.release(); - } - mProximitySensorEnabled = false; - } finally { - Binder.restoreCallingIdentity(identity); - } - if (mProximitySensorActive) { - mProximitySensorActive = false; - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "disableProximityLockLocked mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - forceUserActivityLocked(); - } + + @Override + public void onChange(boolean selfChange, Uri uri) { + synchronized (mLock) { + handleSettingsChangedLocked(); } } } - private void proximityChangedLocked(boolean active) { - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "proximityChangedLocked, active: " + active); + private final WindowManagerPolicy.ScreenOnListener mScreenOnListener = + new WindowManagerPolicy.ScreenOnListener() { + @Override + public void onScreenOn() { } - if (!mProximitySensorEnabled) { - Slog.d(TAG, "Ignoring proximity change after sensor is disabled"); - return; + }; + + /** + * Handler for asynchronous operations performed by the power manager. + */ + private final class PowerManagerHandler extends Handler { + public PowerManagerHandler(Looper looper) { + super(looper); } - if (active) { - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - goToSleepLocked(SystemClock.uptimeMillis(), - WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR); - } - mProximitySensorActive = true; - } else { - // proximity sensor negative events trigger as user activity. - // temporarily set mUserActivityAllowed to true so this will work - // even when the keyguard is on. - mProximitySensorActive = false; - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - forceUserActivityLocked(); - } - if (mProximityWakeLockCount == 0) { - // disable sensor if we have no listeners left after proximity negative - disableProximityLockLocked(); + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_USER_ACTIVITY_TIMEOUT: + handleUserActivityTimeout(); + break; + case MSG_SANDMAN: + handleSandman(); + break; } } } - private void enableLightSensorLocked(boolean enable) { - if (DEBUG_LIGHT_SENSOR) { - Slog.d(TAG, "enableLightSensorLocked enable=" + enable - + " mLightSensorEnabled=" + mLightSensorEnabled - + " mAutoBrightessEnabled=" + mAutoBrightessEnabled - + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor); + /** + * Represents a wake lock that has been acquired by an application. + */ + private final class WakeLock implements IBinder.DeathRecipient { + public final IBinder mLock; + public int mFlags; + public String mTag; + public WorkSource mWorkSource; + public int mOwnerUid; + public int mOwnerPid; + + public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + mLock = lock; + mFlags = flags; + mTag = tag; + mWorkSource = copyWorkSource(workSource); + mOwnerUid = ownerUid; + mOwnerPid = ownerPid; + } + + @Override + public void binderDied() { + PowerManagerService.this.handleWakeLockDeath(this); } - if (!mAutoBrightessEnabled) { - enable = false; + + public boolean hasSameProperties(int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + return mFlags == flags + && mTag.equals(tag) + && hasSameWorkSource(workSource) + && mOwnerUid == ownerUid + && mOwnerPid == ownerPid; } - if (mSensorManager != null && mLightSensorEnabled != enable) { - mLightSensorEnabled = enable; - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - if (enable) { - // reset our highest value when reenabling - mHighestLightSensorValue = -1; - // force recompute of backlight values - final int value = (int)mLightSensorValue; - if (value >= 0) { - mLightSensorValue = -1; - handleLightSensorValue(value, true); - } - mSensorManager.registerListener(mLightListener, mLightSensor, - LIGHT_SENSOR_RATE); - } else { - mSensorManager.unregisterListener(mLightListener); - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - } - } finally { - Binder.restoreCallingIdentity(identity); - } + + public void updateProperties(int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + mFlags = flags; + mTag = tag; + updateWorkSource(workSource); + mOwnerUid = ownerUid; + mOwnerPid = ownerPid; } - } - SensorEventListener mProximityListener = new SensorEventListener() { - public void onSensorChanged(SensorEvent event) { - long milliseconds = SystemClock.elapsedRealtime(); - synchronized (mLocks) { - float distance = event.values[0]; - long timeSinceLastEvent = milliseconds - mLastProximityEventTime; - mLastProximityEventTime = milliseconds; - mHandler.removeCallbacks(mProximityTask); - boolean proximityTaskQueued = false; + public boolean hasSameWorkSource(WorkSource workSource) { + return Objects.equal(mWorkSource, workSource); + } - // compare against getMaximumRange to support sensors that only return 0 or 1 - boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD && - distance < mProximitySensor.getMaximumRange()); + public void updateWorkSource(WorkSource workSource) { + mWorkSource = copyWorkSource(workSource); + } - if (DEBUG_PROXIMITY_SENSOR) { - Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active); - } - if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) { - // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing - mProximityPendingValue = (active ? 1 : 0); - mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent); - proximityTaskQueued = true; - } else { - // process the value immediately - mProximityPendingValue = -1; - proximityChangedLocked(active); - } + @Override + public String toString() { + return getLockLevelString() + + " '" + mTag + "'" + getLockFlagsString() + + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")"; + } - // update mProximityPartialLock state - boolean held = mProximityPartialLock.isHeld(); - if (!held && proximityTaskQueued) { - // hold wakelock until mProximityTask runs - mProximityPartialLock.acquire(); - } else if (held && !proximityTaskQueued) { - mProximityPartialLock.release(); - } + private String getLockLevelString() { + switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + return "FULL_WAKE_LOCK "; + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + return "SCREEN_BRIGHT_WAKE_LOCK "; + case PowerManager.SCREEN_DIM_WAKE_LOCK: + return "SCREEN_DIM_WAKE_LOCK "; + case PowerManager.PARTIAL_WAKE_LOCK: + return "PARTIAL_WAKE_LOCK "; + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return "PROXIMITY_SCREEN_OFF_WAKE_LOCK"; + default: + return "??? "; } } - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // ignore + private String getLockFlagsString() { + String result = ""; + if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + result += " ACQUIRE_CAUSES_WAKEUP"; + } + if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) { + result += " ON_AFTER_RELEASE"; + } + return result; } - }; + } - private void handleLightSensorValue(int value, boolean immediate) { - long milliseconds = SystemClock.elapsedRealtime(); - if (mLightSensorValue == -1 - || milliseconds < mLastScreenOnTime + mLightSensorWarmupTime - || mWaitingForFirstLightSensor) { - // process the value immediately if screen has just turned on - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - lightSensorChangedLocked(value, immediate); - } else { - if ((value > mLightSensorValue && mLightSensorPendingDecrease) || - (value < mLightSensorValue && mLightSensorPendingIncrease) || - (value == mLightSensorValue) || - (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) { - // delay processing to debounce the sensor - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = (value < mLightSensorValue); - mLightSensorPendingIncrease = (value > mLightSensorValue); - if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) { - mLightSensorPendingValue = value; - mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY); + private final class SuspendBlockerImpl implements SuspendBlocker { + private final String mName; + private int mReferenceCount; + + public SuspendBlockerImpl(String name) { + mName = name; + } + + @Override + protected void finalize() throws Throwable { + try { + if (mReferenceCount != 0) { + Log.wtf(TAG, "Suspend blocker \"" + mName + + "\" was finalized without being released!"); + mReferenceCount = 0; + nativeReleaseSuspendBlocker(mName); } - } else { - mLightSensorPendingValue = value; + } finally { + super.finalize(); } } - } - SensorEventListener mLightListener = new SensorEventListener() { @Override - public void onSensorChanged(SensorEvent event) { - if (DEBUG_LIGHT_SENSOR) { - Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]); - } - synchronized (mLocks) { - // ignore light sensor while screen is turning off - if (isScreenTurningOffLocked()) { - return; + public void acquire() { + synchronized (this) { + mReferenceCount += 1; + if (mReferenceCount == 1) { + nativeAcquireSuspendBlocker(mName); } - handleLightSensorValue((int)event.values[0], mWaitingForFirstLightSensor); - if (mWaitingForFirstLightSensor && !mPreparingForScreenOn) { - if (DEBUG_LIGHT_ANIMATION) { - Slog.d(TAG, "onSensorChanged: Clearing mWaitingForFirstLightSensor."); - } - mWaitingForFirstLightSensor = false; + } + } + + @Override + public void release() { + synchronized (this) { + mReferenceCount -= 1; + if (mReferenceCount == 0) { + nativeReleaseSuspendBlocker(mName); + } else if (mReferenceCount < 0) { + Log.wtf(TAG, "Suspend blocker \"" + mName + + "\" was released without being acquired!", new Throwable()); + mReferenceCount = 0; } } } @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // ignore + public String toString() { + synchronized (this) { + return mName + ": ref count=" + mReferenceCount; + } } - }; + } } diff --git a/services/java/com/android/server/power/RampAnimator.java b/services/java/com/android/server/power/RampAnimator.java new file mode 100644 index 0000000..6f063c3 --- /dev/null +++ b/services/java/com/android/server/power/RampAnimator.java @@ -0,0 +1,131 @@ +/* + * 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 android.animation.ValueAnimator; +import android.util.IntProperty; +import android.view.Choreographer; + +/** + * A custom animator that progressively updates a property value at + * a given variable rate until it reaches a particular target value. + */ +final class RampAnimator<T> { + private final T mObject; + private final IntProperty<T> mProperty; + private final Choreographer mChoreographer; + + private int mCurrentValue; + private int mTargetValue; + private int mRate; + + private boolean mAnimating; + private float mAnimatedValue; // higher precision copy of mCurrentValue + private long mLastFrameTimeNanos; + + private boolean mFirstTime = true; + + public RampAnimator(T object, IntProperty<T> property) { + mObject = object; + mProperty = property; + mChoreographer = Choreographer.getInstance(); + } + + /** + * Starts animating towards the specified value. + * + * If this is the first time the property is being set, the value jumps + * directly to the target. + * + * @param target The target value. + * @param rate The convergence rate, in units per second. + * @return True if the target differs from the previous target. + */ + public boolean animateTo(int target, int rate) { + // Immediately jump to the target the first time. + if (mFirstTime) { + mFirstTime = false; + mProperty.setValue(mObject, target); + mCurrentValue = target; + return true; + } + + // Adjust the rate based on the closest target. + // If a faster rate is specified, then use the new rate so that we converge + // more rapidly based on the new request. + // If a slower rate is specified, then use the new rate only if the current + // value is somewhere in between the new and the old target meaning that + // we will be ramping in a different direction to get there. + // Otherwise, continue at the previous rate. + if (!mAnimating + || rate > mRate + || (target <= mCurrentValue && mCurrentValue <= mTargetValue) + || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) { + mRate = rate; + } + + final boolean changed = (mTargetValue != target); + mTargetValue = target; + + // Start animating. + if (!mAnimating && target != mCurrentValue) { + mAnimating = true; + mAnimatedValue = mCurrentValue; + mLastFrameTimeNanos = System.nanoTime(); + postCallback(); + } + + return changed; + } + + private void postCallback() { + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null); + } + + private final Runnable mCallback = new Runnable() { + @Override // Choreographer callback + public void run() { + final long frameTimeNanos = mChoreographer.getFrameTimeNanos(); + final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) + * 0.000000001f; + final float amount = timeDelta * mRate / ValueAnimator.getDurationScale(); + mLastFrameTimeNanos = frameTimeNanos; + + // Advance the animated value towards the target at the specified rate + // and clamp to the target. This gives us the new current value but + // we keep the animated value around to allow for fractional increments + // towards the target. + int oldCurrentValue = mCurrentValue; + if (mTargetValue > mCurrentValue) { + mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue); + } else { + mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue); + } + mCurrentValue = (int)Math.round(mAnimatedValue); + + if (oldCurrentValue != mCurrentValue) { + mProperty.setValue(mObject, mCurrentValue); + } + + if (mTargetValue != mCurrentValue) { + postCallback(); + } else { + mAnimating = false; + } + } + }; +} diff --git a/services/java/com/android/server/power/SuspendBlocker.java b/services/java/com/android/server/power/SuspendBlocker.java new file mode 100644 index 0000000..70b278a --- /dev/null +++ b/services/java/com/android/server/power/SuspendBlocker.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Low-level suspend blocker mechanism equivalent to holding a partial wake lock. + * + * This interface is used internally to avoid introducing internal dependencies + * on the high-level wake lock mechanism. + */ +interface SuspendBlocker { + /** + * Acquires the suspend blocker. + * Prevents the CPU from going to sleep. + * + * Calls to acquire() nest and must be matched by the same number + * of calls to release(). + */ + void acquire(); + + /** + * Releases the suspend blocker. + * Allows the CPU to go to sleep if no other suspend blockers are held. + * + * It is an error to call release() if the suspend blocker has not been acquired. + * The system may crash. + */ + void release(); +} diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 9e62a16..40a9eed 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -855,7 +855,7 @@ public class WindowManagerService extends IWindowManager.Stub android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); android.os.Process.setCanSelfBackground(false); - mPolicy.init(mContext, mService, mService, mPM); + mPolicy.init(mContext, mService, mService); mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; @@ -910,7 +910,7 @@ public class WindowManagerService extends IWindowManager.Stub mContext.registerReceiver(mBroadcastReceiver, filter); mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG"); + | PowerManager.ON_AFTER_RELEASE, PowerManager.KEEP_SCREEN_ON_FLAG_TAG); mHoldingScreenWakeLock.setReferenceCounted(false); mInputManager = new InputManagerService(context, mInputMonitor); @@ -9074,16 +9074,16 @@ public class WindowManagerService extends IWindowManager.Stub setHoldScreenLocked(mInnerFields.mHoldScreen != null); if (!mDisplayFrozen) { if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) { - mPowerManager.setScreenBrightnessOverride(-1); + mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1); } else { - mPowerManager.setScreenBrightnessOverride((int) - (mInnerFields.mScreenBrightness * PowerManager.BRIGHTNESS_ON)); + mPowerManager.setScreenBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mScreenBrightness)); } if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) { - mPowerManager.setButtonBrightnessOverride(-1); + mPowerManager.setButtonBrightnessOverrideFromWindowManager(-1); } else { - mPowerManager.setButtonBrightnessOverride((int) - (mInnerFields.mButtonBrightness * PowerManager.BRIGHTNESS_ON)); + mPowerManager.setButtonBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mButtonBrightness)); } } if (mInnerFields.mHoldScreen != mHoldingScreenOn) { @@ -9094,8 +9094,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mTurnOnScreen) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!"); - mPowerManager.userActivity(SystemClock.uptimeMillis(), false, - PowerManager.USER_ACTIVITY_EVENT_BUTTON, true); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); mTurnOnScreen = false; } @@ -9126,6 +9125,10 @@ public class WindowManagerService extends IWindowManager.Stub } } + private int toBrightnessOverride(float value) { + return (int)(value * PowerManager.BRIGHTNESS_ON); + } + void checkDrawnWindowsLocked() { if (mWaitingForDrawn.size() > 0) { for (int j=mWaitingForDrawn.size()-1; j>=0; j--) { diff --git a/services/jni/com_android_server_LightsService.cpp b/services/jni/com_android_server_LightsService.cpp index 9ed4951..401e1aa 100644 --- a/services/jni/com_android_server_LightsService.cpp +++ b/services/jni/com_android_server_LightsService.cpp @@ -120,7 +120,10 @@ static void setLight_native(JNIEnv *env, jobject clazz, int ptr, state.flashOffMS = offMS; state.brightnessMode = brightnessMode; - devices->lights[light]->set_light(devices->lights[light], &state); + { + ALOGD_IF_SLOW(50, "Excessive delay setting light"); + devices->lights[light]->set_light(devices->lights[light], &state); + } } static JNINativeMethod method_table[] = { diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp index 35c2142..701b15a 100644 --- a/services/jni/com_android_server_input_InputManagerService.cpp +++ b/services/jni/com_android_server_input_InputManagerService.cpp @@ -143,7 +143,7 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl enum { WM_ACTION_PASS_TO_USER = 1, - WM_ACTION_POKE_USER_ACTIVITY = 2, + WM_ACTION_WAKE_UP = 2, WM_ACTION_GO_TO_SLEEP = 4, }; @@ -899,11 +899,11 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, android_server_PowerManagerService_goToSleep(when); } - if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { + if (wmActions & WM_ACTION_WAKE_UP) { #if DEBUG_INPUT_DISPATCHER_POLICY - ALOGD("handleInterceptActions: Poking user activity."); + ALOGD("handleInterceptActions: Waking up."); #endif - android_server_PowerManagerService_userActivity(when, USER_ACTIVITY_EVENT_BUTTON); + android_server_PowerManagerService_wakeUp(when); } if (wmActions & WM_ACTION_PASS_TO_USER) { diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp index 8307d25..3f3970b 100644 --- a/services/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/jni/com_android_server_power_PowerManagerService.cpp @@ -21,6 +21,8 @@ #include "JNIHelp.h" #include "jni.h" +#include <ScopedUtfChars.h> + #include <limits.h> #include <android_runtime/AndroidRuntime.h> @@ -28,6 +30,7 @@ #include <utils/Timers.h> #include <utils/misc.h> #include <utils/String8.h> +#include <utils/Log.h> #include <hardware/power.h> #include <hardware_legacy/power.h> #include <cutils/android_reboot.h> @@ -42,8 +45,9 @@ namespace android { // ---------------------------------------------------------------------------- static struct { - jmethodID goToSleep; - jmethodID userActivity; + jmethodID wakeUpFromNative; + jmethodID goToSleepFromNative; + jmethodID userActivityFromNative; } gPowerManagerServiceClassInfo; // ---------------------------------------------------------------------------- @@ -106,19 +110,32 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity, - nanoseconds_to_milliseconds(eventTime), false, eventType, false); - checkAndClearExceptionFromCallback(env, "userActivity"); + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.userActivityFromNative, + nanoseconds_to_milliseconds(eventTime), eventType, 0); + checkAndClearExceptionFromCallback(env, "userActivityFromNative"); } } -void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) { +void android_server_PowerManagerService_wakeUp(nsecs_t eventTime) { if (gPowerManagerServiceObj) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep, + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.wakeUpFromNative, nanoseconds_to_milliseconds(eventTime)); - checkAndClearExceptionFromCallback(env, "goToSleep"); + checkAndClearExceptionFromCallback(env, "wakeUpFromNative"); + } +} + +void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) { + if (gPowerManagerServiceObj) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.goToSleepFromNative, + nanoseconds_to_milliseconds(eventTime), 0); + checkAndClearExceptionFromCallback(env, "goToSleepFromNative"); } } @@ -137,68 +154,62 @@ static void nativeInit(JNIEnv* env, jobject obj) { } static void nativeSetPowerState(JNIEnv* env, - jobject serviceObj, jboolean screenOn, jboolean screenBright) { + jclass clazz, jboolean screenOn, jboolean screenBright) { AutoMutex _l(gPowerManagerLock); gScreenOn = screenOn; gScreenBright = screenBright; } -static void nativeStartSurfaceFlingerAnimation(JNIEnv* env, - jobject obj, jint mode) { - // this is not handled by surfaceflinger anymore +static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) { + ScopedUtfChars name(env, nameStr); + acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); } -static void nativeAcquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj) { - if (idObj == NULL) { - jniThrowNullPointerException(env, "id is null"); - return; - } - - const char *id = env->GetStringUTFChars(idObj, NULL); - - acquire_wake_lock(lock, id); - - env->ReleaseStringUTFChars(idObj, id); +static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) { + ScopedUtfChars name(env, nameStr); + release_wake_lock(name.c_str()); } -static void nativeReleaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj) { - if (idObj == NULL) { - jniThrowNullPointerException(env, "id is null"); - return ; - } - - const char *id = env->GetStringUTFChars(idObj, NULL); - - release_wake_lock(id); - - env->ReleaseStringUTFChars(idObj, id); - -} - -static int nativeSetScreenState(JNIEnv *env, jobject clazz, jboolean on) { +static void nativeSetScreenState(JNIEnv *env, jclass clazz, jboolean on) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (on) { - autosuspend_disable(); + { + ALOGD_IF_SLOW(50, "Excessive delay in autosuspend_disable() while turning screen on"); + autosuspend_disable(); + } + if (gPowerModule) { + ALOGD_IF_SLOW(10, "Excessive delay in setInteractive(true) while turning screen on"); gPowerModule->setInteractive(gPowerModule, true); } - s->unblank(); + + { + ALOGD_IF_SLOW(20, "Excessive delay in unblank() while turning screen on"); + s->unblank(); + } } else { - s->blank(); + { + ALOGD_IF_SLOW(20, "Excessive delay in blank() while turning screen off"); + s->blank(); + } + if (gPowerModule) { + ALOGD_IF_SLOW(10, "Excessive delay in setInteractive(false) while turning screen off"); gPowerModule->setInteractive(gPowerModule, false); } - autosuspend_enable(); - } - return 0; + { + ALOGD_IF_SLOW(50, "Excessive delay in autosuspend_enable() while turning screen off"); + autosuspend_enable(); + } + } } -static void nativeShutdown(JNIEnv *env, jobject clazz) { +static void nativeShutdown(JNIEnv *env, jclass clazz) { android_reboot(ANDROID_RB_POWEROFF, 0, 0); } -static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) { +static void nativeReboot(JNIEnv *env, jclass clazz, jstring reason) { if (reason == NULL) { android_reboot(ANDROID_RB_RESTART, 0, 0); } else { @@ -218,13 +229,11 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeInit }, { "nativeSetPowerState", "(ZZ)V", (void*) nativeSetPowerState }, - { "nativeStartSurfaceFlingerAnimation", "(I)V", - (void*) nativeStartSurfaceFlingerAnimation }, - { "nativeAcquireWakeLock", "(ILjava/lang/String;)V", - (void*) nativeAcquireWakeLock }, - { "nativeReleaseWakeLock", "(Ljava/lang/String;)V", - (void*) nativeReleaseWakeLock }, - { "nativeSetScreenState", "(Z)I", + { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", + (void*) nativeAcquireSuspendBlocker }, + { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", + (void*) nativeReleaseSuspendBlocker }, + { "nativeSetScreenState", "(Z)V", (void*) nativeSetScreenState }, { "nativeShutdown", "()V", (void*) nativeShutdown }, @@ -254,11 +263,14 @@ int register_android_server_PowerManagerService(JNIEnv* env) { jclass clazz; FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, clazz, - "goToSleep", "(J)V"); + GET_METHOD_ID(gPowerManagerServiceClassInfo.wakeUpFromNative, clazz, + "wakeUpFromNative", "(J)V"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleepFromNative, clazz, + "goToSleepFromNative", "(JI)V"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, clazz, - "userActivity", "(JZIZ)V"); + GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, + "userActivityFromNative", "(JII)V"); // Initialize for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { diff --git a/services/jni/com_android_server_power_PowerManagerService.h b/services/jni/com_android_server_power_PowerManagerService.h index cc3b5ef5..0808b80 100644 --- a/services/jni/com_android_server_power_PowerManagerService.h +++ b/services/jni/com_android_server_power_PowerManagerService.h @@ -27,6 +27,7 @@ namespace android { extern bool android_server_PowerManagerService_isScreenOn(); extern bool android_server_PowerManagerService_isScreenBright(); extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType); +extern void android_server_PowerManagerService_wakeUp(nsecs_t eventTime); extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime); } // namespace android |