auto import from //depot/cupcake/@135843
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
new file mode 100644
index 0000000..ad30ffc
--- /dev/null
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -0,0 +1,1862 @@
+/*
+ * Copyright (C) 2007 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;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.server.am.BatteryStatsService;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+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.Power;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.provider.Settings.SettingNotFoundException;
+import android.provider.Settings;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.WindowManagerPolicy;
+import static android.provider.Settings.System.DIM_SCREEN;
+import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Observable;
+import java.util.Observer;
+
+class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor {
+
+ private static final String TAG = "PowerManagerService";
+ static final String PARTIAL_NAME = "PowerManagerService";
+
+ private static final boolean LOG_PARTIAL_WL = false;
+
+ // Indicates whether touch-down cycles should be logged as part of the
+ // LOG_POWER_SCREEN_STATE log events
+ private static final boolean LOG_TOUCH_DOWNS = true;
+
+ private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
+ | PowerManager.SCREEN_DIM_WAKE_LOCK
+ | PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+ | PowerManager.FULL_WAKE_LOCK;
+
+ // time since last state: time since last event:
+ // The short keylight delay comes from Gservices; 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
+
+ // Cached Gservices settings; see updateGservicesValues()
+ private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
+
+ // flags for setPowerState
+ 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;
+
+ static final boolean ANIMATE_SCREEN_LIGHTS = true;
+ static final boolean ANIMATE_BUTTON_LIGHTS = false;
+ static final boolean ANIMATE_KEYBOARD_LIGHTS = false;
+
+ static final int ANIM_STEPS = 60/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 = Power.BRIGHTNESS_OFF;
+ static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
+
+ static final int LOG_POWER_SLEEP_REQUESTED = 2724;
+ static final int LOG_POWER_SCREEN_BROADCAST_SEND = 2725;
+ static final int LOG_POWER_SCREEN_BROADCAST_DONE = 2726;
+ static final int LOG_POWER_SCREEN_BROADCAST_STOP = 2727;
+ static final int LOG_POWER_SCREEN_STATE = 2728;
+ static final int LOG_POWER_PARTIAL_WAKE_STATE = 2729;
+
+ private final int MY_UID;
+
+ private boolean mDoneBooting = false;
+ private int mStayOnConditions = 0;
+ private int mNotificationQueue = -1;
+ private int mNotificationWhy;
+ private int mPartialCount = 0;
+ private int mPowerState;
+ private boolean mOffBecauseOfUser;
+ private int mUserState;
+ private boolean mKeyboardVisible = false;
+ private boolean mUserActivityAllowed = true;
+ private int mTotalDelaySetting;
+ 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 Context mContext;
+ private UnsynchronizedWakeLock mBroadcastWakeLock;
+ private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
+ private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
+ private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private TimeoutTask mTimeoutTask = new TimeoutTask();
+ private LightAnimator mLightAnimator = new LightAnimator();
+ private final BrightnessState mScreenBrightness
+ = new BrightnessState(Power.SCREEN_LIGHT);
+ private final BrightnessState mKeyboardBrightness
+ = new BrightnessState(Power.KEYBOARD_LIGHT);
+ private final BrightnessState mButtonBrightness
+ = new BrightnessState(Power.BUTTON_LIGHT);
+ private boolean mIsPowered = false;
+ private IActivityManager mActivityService;
+ private IBatteryStats mBatteryStats;
+ private BatteryService mBatteryService;
+ private boolean mDimScreen = true;
+ private long mNextTimeout;
+ private volatile int mPokey = 0;
+ private volatile boolean mPokeAwakeOnSet = false;
+ private volatile boolean mInitComplete = false;
+ private HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
+ private long mScreenOnTime;
+ private long mScreenOnStartTime;
+ private boolean mPreventScreenOn;
+ private int mScreenBrightnessOverride = -1;
+
+ // Used when logging number and duration of touch-down cycles
+ private long mTotalTouchDownTime;
+ private long mLastTouchDown;
+ private int mTouchCycles;
+
+ // could be either static or controllable at runtime
+ private static final boolean mSpew = false;
+
+ /*
+ static PrintStream mLog;
+ static {
+ try {
+ mLog = new PrintStream("/data/power.log");
+ }
+ catch (FileNotFoundException e) {
+ android.util.Log.e(TAG, "Life is hard", e);
+ }
+ }
+ static class Log {
+ static void d(String tag, String s) {
+ mLog.println(s);
+ android.util.Log.d(tag, s);
+ }
+ static void i(String tag, String s) {
+ mLog.println(s);
+ android.util.Log.i(tag, s);
+ }
+ static void w(String tag, String s) {
+ mLog.println(s);
+ android.util.Log.w(tag, s);
+ }
+ static void e(String tag, String s) {
+ mLog.println(s);
+ android.util.Log.e(tag, s);
+ }
+ }
+ */
+
+ /**
+ * 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;
+
+ UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) {
+ mFlags = flags;
+ mTag = tag;
+ mToken = new Binder();
+ mRefCounted = refCounted;
+ }
+
+ public void acquire() {
+ if (!mRefCounted || mCount++ == 0) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
+ MY_UID, mTag);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void release() {
+ if (!mRefCounted || --mCount == 0) {
+ PowerManagerService.this.releaseWakeLockLocked(mToken, false);
+ }
+ if (mCount < 0) {
+ throw new RuntimeException("WakeLock under-locked " + mTag);
+ }
+ }
+
+ public String toString() {
+ return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags)
+ + " mCount=" + mCount + ")";
+ }
+ }
+
+ 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.
+ // temporarily set mUserActivityAllowed to true so this will work
+ // even when the keyguard is on.
+ synchronized (mLocks) {
+ boolean savedActivityAllowed = mUserActivityAllowed;
+ mUserActivityAllowed = true;
+ userActivity(SystemClock.uptimeMillis(), false);
+ mUserActivityAllowed = savedActivityAllowed;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ private class SettingsObserver implements Observer {
+ private int getInt(String name) {
+ return mSettings.getValues(name).getAsInteger(Settings.System.VALUE);
+ }
+
+ public void update(Observable o, Object arg) {
+ synchronized (mLocks) {
+ // STAY_ON_WHILE_PLUGGED_IN
+ mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN);
+ updateWakeLockLocked();
+
+ // SCREEN_OFF_TIMEOUT
+ mTotalDelaySetting = getInt(SCREEN_OFF_TIMEOUT);
+
+ // DIM_SCREEN
+ //mDimScreen = getInt(DIM_SCREEN) != 0;
+
+ // recalculate everything
+ setScreenOffTimeoutsLocked();
+ }
+ }
+ }
+
+ PowerManagerService()
+ {
+ // Hack to get our uid... should have a func for this.
+ long token = Binder.clearCallingIdentity();
+ MY_UID = Binder.getCallingUid();
+ Binder.restoreCallingIdentity(token);
+
+ // XXX remove this when the kernel doesn't timeout wake locks
+ Power.setLastUserActivityTimeout(7*24*3600*1000); // one week
+
+ // assume nothing is on yet
+ mUserState = mPowerState = 0;
+
+ // Add ourself to the Watchdog monitors.
+ Watchdog.getInstance().addMonitor(this);
+ mScreenOnStartTime = SystemClock.elapsedRealtime();
+ }
+
+ private ContentQueryMap mSettings;
+
+ void init(Context context, IActivityManager activity, BatteryService battery) {
+ mContext = context;
+ mActivityService = activity;
+ mBatteryStats = BatteryStatsService.getService();
+ mBatteryService = battery;
+
+ mHandlerThread = new HandlerThread("PowerManagerService") {
+ @Override
+ protected void onLooperPrepared() {
+ super.onLooperPrepared();
+ initInThread();
+ }
+ };
+ mHandlerThread.start();
+
+ synchronized (mHandlerThread) {
+ while (!mInitComplete) {
+ try {
+ mHandlerThread.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ void initInThread() {
+ mHandler = new Handler();
+
+ mBroadcastWakeLock = new UnsynchronizedWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, "sleep_notification", 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);
+
+ mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
+ mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
+ mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null,
+ "(" + Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?)",
+ new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN},
+ 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);
+
+ // Listen for Gservices changes
+ IntentFilter gservicesChangedFilter =
+ new IntentFilter(Settings.Gservices.CHANGED_ACTION);
+ mContext.registerReceiver(new GservicesChangedReceiver(), gservicesChangedFilter);
+ // And explicitly do the initial update of our cached settings
+ updateGservicesValues();
+
+ // turn everything on
+ setPowerState(ALL_BRIGHT);
+
+ synchronized (mHandlerThread) {
+ mInitComplete = true;
+ mHandlerThread.notifyAll();
+ }
+ }
+
+ private class WakeLock implements IBinder.DeathRecipient
+ {
+ WakeLock(int f, IBinder b, String t, int u) {
+ super();
+ flags = f;
+ binder = b;
+ tag = t;
+ uid = u == MY_UID ? Process.SYSTEM_UID : u;
+ 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, true);
+ }
+ }
+ final int flags;
+ final IBinder binder;
+ final String tag;
+ final int uid;
+ final int monitorType;
+ boolean activated = true;
+ int minState;
+ }
+
+ private void updateWakeLockLocked() {
+ if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
+ // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set.
+ mStayOnWhilePluggedInScreenDimLock.acquire();
+ mStayOnWhilePluggedInPartialLock.acquire();
+ } else {
+ mStayOnWhilePluggedInScreenDimLock.release();
+ mStayOnWhilePluggedInPartialLock.release();
+ }
+ }
+
+ 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;
+ }
+
+ public void acquireWakeLock(int flags, IBinder lock, String tag) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+ int uid = Binder.getCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ acquireWakeLockLocked(flags, lock, uid, tag);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+ int acquireUid = -1;
+ String acquireName = null;
+ int acquireType = -1;
+
+ if (mSpew) {
+ Log.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
+ }
+
+ int index = mLocks.getIndex(lock);
+ WakeLock wl;
+ boolean newlock;
+ if (index < 0) {
+ wl = new WakeLock(flags, lock, tag, uid);
+ switch (wl.flags & LOCK_MASK)
+ {
+ case PowerManager.FULL_WAKE_LOCK:
+ 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:
+ break;
+ default:
+ // just log and bail. we're in the server, so don't
+ // throw an exception.
+ Log.e(TAG, "bad wakelock type for lock '" + tag + "' "
+ + " flags=" + flags);
+ return;
+ }
+ mLocks.addLock(wl);
+ newlock = true;
+ } else {
+ wl = mLocks.get(index);
+ newlock = false;
+ }
+ 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 ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+ reactivateWakeLocksLocked();
+ if (mSpew) {
+ Log.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
+ + " mLocks.gatherState()=0x"
+ + Integer.toHexString(mLocks.gatherState())
+ + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ }
+ mWakeLockState = mLocks.gatherState();
+ } else {
+ if (mSpew) {
+ Log.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);
+ }
+ else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
+ if (newlock) {
+ mPartialCount++;
+ if (mPartialCount == 1) {
+ if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 1, tag);
+ }
+ }
+ Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
+ }
+ if (newlock) {
+ acquireUid = wl.uid;
+ acquireName = wl.tag;
+ acquireType = wl.monitorType;
+ }
+
+ if (acquireType >= 0) {
+ try {
+ mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+ }
+
+ public void releaseWakeLock(IBinder lock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+
+ synchronized (mLocks) {
+ releaseWakeLockLocked(lock, false);
+ }
+ }
+
+ private void releaseWakeLockLocked(IBinder lock, boolean death) {
+ int releaseUid;
+ String releaseName;
+ int releaseType;
+
+ WakeLock wl = mLocks.removeLock(lock);
+ if (wl == null) {
+ return;
+ }
+
+ if (mSpew) {
+ Log.d(TAG, "releaseWakeLock flags=0x"
+ + Integer.toHexString(wl.flags) + " tag=" + wl.tag);
+ }
+
+ if (isScreenLock(wl.flags)) {
+ mWakeLockState = mLocks.gatherState();
+ // goes in the middle to reduce flicker
+ if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
+ userActivity(SystemClock.uptimeMillis(), false);
+ }
+ setPowerState(mWakeLockState | mUserState);
+ }
+ else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
+ mPartialCount--;
+ if (mPartialCount == 0) {
+ if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
+ Power.releaseWakeLock(PARTIAL_NAME);
+ }
+ }
+ // Unlink the lock from the binder.
+ wl.binder.unlinkToDeath(wl, 0);
+ releaseUid = wl.uid;
+ releaseName = wl.tag;
+ releaseType = wl.monitorType;
+
+ if (releaseType >= 0) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+ } catch (RemoteException e) {
+ // Ignore
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ private void reactivateWakeLocksLocked()
+ {
+ int N = mLocks.size();
+ for (int i=0; i<N; i++) {
+ WakeLock wl = mLocks.get(i);
+ if (isScreenLock(wl.flags)) {
+ mLocks.get(i).activated = true;
+ }
+ }
+ }
+
+ 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();
+ }
+ }
+ 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) {
+ Log.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");
+ }
+
+ 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 {
+ mPokeLocks.remove(token);
+ }
+
+ int oldPokey = mPokey;
+ int cumulative = 0;
+ boolean oldAwakeOnSet = mPokeAwakeOnSet;
+ boolean awakeOnSet = false;
+ for (PokeLock p: mPokeLocks.values()) {
+ cumulative |= p.pokey;
+ if (p.awakeOnSet) {
+ awakeOnSet = true;
+ }
+ }
+ mPokey = cumulative;
+ mPokeAwakeOnSet = awakeOnSet;
+
+ int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK;
+ int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK;
+
+ 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 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 ";
+ default:
+ return "??? ";
+ }
+ }
+
+ 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 & 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;
+ }
+
+ long now = SystemClock.uptimeMillis();
+
+ 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);
+ pw.println(" mOffBecauseOfUser=" + mOffBecauseOfUser
+ + " mUserState=" + mUserState);
+ pw.println(" mNotificationQueue=" + mNotificationQueue
+ + " mNotificationWhy=" + mNotificationWhy);
+ 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);
+ pw.println(" mTotalDelaySetting=" + mTotalDelaySetting);
+ pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock);
+ pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
+ pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
+ pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
+ mScreenBrightness.dump(pw, " mScreenBrightness: ");
+ mKeyboardBrightness.dump(pw, " mKeyboardBrightness: ");
+ mButtonBrightness.dump(pw, " mButtonBrightness: ");
+
+ 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 + ")");
+ }
+
+ pw.println();
+ pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":");
+ for (PokeLock p: mPokeLocks.values()) {
+ pw.println(" poke lock '" + p.tag + "':"
+ + ((p.pokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0
+ ? " POKE_LOCK_IGNORE_CHEEK_EVENTS" : "")
+ + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0
+ ? " POKE_LOCK_SHORT_TIMEOUT" : "")
+ + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0
+ ? " POKE_LOCK_MEDIUM_TIMEOUT" : ""));
+ }
+
+ pw.println();
+ }
+
+ private void setTimeoutLocked(long now, int nextState)
+ {
+ if (mDoneBooting) {
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ long when = now;
+ switch (nextState)
+ {
+ case SCREEN_BRIGHT:
+ when += mKeylightDelay;
+ break;
+ case SCREEN_DIM:
+ if (mDimDelay >= 0) {
+ when += mDimDelay;
+ break;
+ } else {
+ Log.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ }
+ case SCREEN_OFF:
+ synchronized (mLocks) {
+ when += mScreenOffDelay;
+ }
+ break;
+ }
+ if (mSpew) {
+ Log.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState
+ + " when=" + when);
+ }
+ mHandler.postAtTime(mTimeoutTask, when);
+ mNextTimeout = when; // for debugging
+ }
+ }
+
+ private void cancelTimerLocked()
+ {
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = -1;
+ }
+
+ private class TimeoutTask implements Runnable
+ {
+ int nextState; // access should be synchronized on mLocks
+ public void run()
+ {
+ synchronized (mLocks) {
+ if (mSpew) {
+ Log.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, SCREEN_DIM);
+ break;
+ }
+ case SCREEN_DIM:
+ setTimeoutLocked(now, SCREEN_OFF);
+ break;
+ }
+ }
+ }
+ }
+
+ private void sendNotificationLocked(boolean on, int why)
+ {
+
+ if (!on) {
+ mNotificationWhy = why;
+ }
+
+ int value = on ? 1 : 0;
+ if (mNotificationQueue == -1) {
+ // empty
+ // Acquire the broadcast wake lock before changing the power
+ // state. It will be release after the broadcast is sent.
+ mBroadcastWakeLock.acquire();
+ EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount);
+ mNotificationQueue = value;
+ mHandler.post(mNotificationTask);
+ } else if (mNotificationQueue != value) {
+ // it's a pair, so cancel it
+ mNotificationQueue = -1;
+ mHandler.removeCallbacks(mNotificationTask);
+ EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
+ mBroadcastWakeLock.release();
+ } else {
+ // else, same so do nothing -- maybe we should warn?
+ Log.w(TAG, "Duplicate notification: on=" + on + " why=" + why);
+ }
+ }
+
+ private Runnable mNotificationTask = new Runnable()
+ {
+ public void run()
+ {
+ int value;
+ int why;
+ WindowManagerPolicy policy;
+ synchronized (mLocks) {
+ policy = getPolicyLocked();
+ value = mNotificationQueue;
+ why = mNotificationWhy;
+ mNotificationQueue = -1;
+ }
+ if (value == 1) {
+ mScreenOnStart = SystemClock.uptimeMillis();
+
+ policy.screenTurnedOn();
+ try {
+ ActivityManagerNative.getDefault().wakingUp();
+ } catch (RemoteException e) {
+ // ignore it
+ }
+
+ if (mSpew) {
+ Log.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock);
+ }
+ if (mContext != null && ActivityManagerNative.isSystemReady()) {
+ mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+ mScreenOnBroadcastDone, mHandler, 0, null, null);
+ } else {
+ synchronized (mLocks) {
+ EventLog.writeEvent(LOG_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(LOG_POWER_SCREEN_BROADCAST_STOP, 3,
+ mBroadcastWakeLock.mCount);
+ 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
+ // in sendNotificationLocked.
+ }
+ }
+ };
+
+ long mScreenOnStart;
+ private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLocks) {
+ EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_DONE, 1,
+ SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount);
+ mBroadcastWakeLock.release();
+ }
+ }
+ };
+
+ long mScreenOffStart;
+ private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLocks) {
+ EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_DONE, 0,
+ SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
+ mBroadcastWakeLock.release();
+ }
+ }
+ };
+
+ void logPointerUpEvent() {
+ if (LOG_TOUCH_DOWNS) {
+ mTotalTouchDownTime += SystemClock.elapsedRealtime() - mLastTouchDown;
+ mLastTouchDown = 0;
+ }
+ }
+
+ void logPointerDownEvent() {
+ if (LOG_TOUCH_DOWNS) {
+ // If we are not already timing a down/up sequence
+ if (mLastTouchDown == 0) {
+ mLastTouchDown = SystemClock.elapsedRealtime();
+ mTouchCycles++;
+ }
+ }
+ }
+
+ /**
+ * 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 Power.setScreenState(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 ((mPowerState & SCREEN_ON_BIT) != 0) {
+ if (mSpew) {
+ Log.d(TAG,
+ "preventScreenOn: turning on after a prior preventScreenOn(true)!");
+ }
+ int err = Power.setScreenState(true);
+ if (err != 0) {
+ Log.w(TAG, "preventScreenOn: error from Power.setScreenState(): " + err);
+ }
+ }
+
+ // Release the partial wake lock that we held during the
+ // preventScreenOn(true) -> preventScreenOn(false) sequence.
+ mPreventScreenOnPartialLock.release();
+ }
+ }
+ }
+
+ public void setScreenBrightnessOverride(int brightness) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ synchronized (mLocks) {
+ if (mScreenBrightnessOverride != brightness) {
+ mScreenBrightnessOverride = brightness;
+ updateLightsLocked(mPowerState, SCREEN_ON_BIT);
+ }
+ }
+ }
+
+ /**
+ * 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).
+ */
+ 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) {
+ Log.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do");
+ 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.
+ Log.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! "
+ + "Forcing the screen back on...");
+ preventScreenOn(false);
+ }
+
+ private Runnable mForceReenableScreenTask = new Runnable() {
+ public void run() {
+ forceReenableScreen();
+ }
+ };
+
+ private void setPowerState(int state)
+ {
+ setPowerState(state, false, false);
+ }
+
+ private void setPowerState(int newState, boolean noChangeLights, boolean becauseOfUser)
+ {
+ synchronized (mLocks) {
+ int err;
+
+ if (mSpew) {
+ Log.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState)
+ + " newState=0x" + Integer.toHexString(newState)
+ + " noChangeLights=" + noChangeLights);
+ }
+
+ if (noChangeLights) {
+ newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK);
+ }
+
+ if (batteryIsLow()) {
+ newState |= BATTERY_LOW_BIT;
+ } else {
+ newState &= ~BATTERY_LOW_BIT;
+ }
+ if (newState == mPowerState) {
+ return;
+ }
+
+ if (!mDoneBooting) {
+ newState |= ALL_BRIGHT;
+ }
+
+ boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;
+ boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0;
+
+ if (mSpew) {
+ Log.d(TAG, "setPowerState: mPowerState=" + mPowerState
+ + " newState=" + newState + " noChangeLights=" + noChangeLights);
+ Log.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0)
+ + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0));
+ Log.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0)
+ + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0));
+ Log.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0)
+ + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0));
+ Log.d(TAG, " oldScreenOn=" + oldScreenOn
+ + " newScreenOn=" + newScreenOn);
+ Log.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0)
+ + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0));
+ }
+
+ if (mPowerState != newState) {
+ err = updateLightsLocked(newState, 0);
+ if (err != 0) {
+ return;
+ }
+ mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
+ }
+
+ if (oldScreenOn != newScreenOn) {
+ if (newScreenOn) {
+ // 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 (mSpew) {
+ Log.d(TAG, "- turning screen on... mPreventScreenOn = "
+ + mPreventScreenOn);
+ }
+
+ if (mPreventScreenOn) {
+ if (mSpew) {
+ Log.d(TAG, "- PREVENTING screen from really turning on!");
+ }
+ reallyTurnScreenOn = false;
+ }
+ if (reallyTurnScreenOn) {
+ err = Power.setScreenState(true);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenOn();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ Power.setScreenState(false);
+ // But continue as if we really did turn the screen on...
+ err = 0;
+ }
+
+ mScreenOnStartTime = SystemClock.elapsedRealtime();
+ mLastTouchDown = 0;
+ mTotalTouchDownTime = 0;
+ mTouchCycles = 0;
+ EventLog.writeEvent(LOG_POWER_SCREEN_STATE, 1, becauseOfUser ? 1 : 0,
+ mTotalTouchDownTime, mTouchCycles);
+ if (err == 0) {
+ mPowerState |= SCREEN_ON_BIT;
+ sendNotificationLocked(true, -1);
+ }
+ } else {
+ mScreenOffTime = SystemClock.elapsedRealtime();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenOff();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ mPowerState &= ~SCREEN_ON_BIT;
+ if (!mScreenBrightness.animating) {
+ err = screenOffFinishedAnimating(becauseOfUser);
+ } else {
+ mOffBecauseOfUser = becauseOfUser;
+ err = 0;
+ mLastTouchDown = 0;
+ }
+ }
+ }
+ }
+ }
+
+ private int screenOffFinishedAnimating(boolean becauseOfUser) {
+ // 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(LOG_POWER_SCREEN_STATE, 0, becauseOfUser ? 1 : 0,
+ mTotalTouchDownTime, mTouchCycles);
+ mLastTouchDown = 0;
+ int err = Power.setScreenState(false);
+ if (mScreenOnStartTime != 0) {
+ mScreenOnTime += SystemClock.elapsedRealtime() - mScreenOnStartTime;
+ mScreenOnStartTime = 0;
+ }
+ if (err == 0) {
+ int why = becauseOfUser
+ ? WindowManagerPolicy.OFF_BECAUSE_OF_USER
+ : WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
+ sendNotificationLocked(false, why);
+ }
+ return err;
+ }
+
+ private boolean batteryIsLow() {
+ return (!mIsPowered &&
+ mBatteryService.getBatteryLevel() <= Power.LOW_BATTERY_THRESHOLD);
+ }
+
+ private int updateLightsLocked(int newState, int forceState) {
+ int oldState = mPowerState;
+ int difference = (newState ^ oldState) | forceState;
+ if (difference == 0) {
+ return 0;
+ }
+
+ int offMask = 0;
+ int dimMask = 0;
+ int onMask = 0;
+
+ int preferredBrightness = getPreferredBrightness();
+ boolean startAnimation = false;
+
+ if ((difference & KEYBOARD_BRIGHT_BIT) != 0) {
+ if (ANIMATE_KEYBOARD_LIGHTS) {
+ if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
+ mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
+ ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS);
+ } else {
+ mKeyboardBrightness.setTargetLocked(preferredBrightness,
+ ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS);
+ }
+ startAnimation = true;
+ } else {
+ if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
+ offMask |= Power.KEYBOARD_LIGHT;
+ } else {
+ onMask |= Power.KEYBOARD_LIGHT;
+ }
+ }
+ }
+
+ if ((difference & BUTTON_BRIGHT_BIT) != 0) {
+ if (ANIMATE_BUTTON_LIGHTS) {
+ if ((newState & BUTTON_BRIGHT_BIT) == 0) {
+ mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
+ ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS);
+ } else {
+ mButtonBrightness.setTargetLocked(preferredBrightness,
+ ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS);
+ }
+ startAnimation = true;
+ } else {
+ if ((newState & BUTTON_BRIGHT_BIT) == 0) {
+ offMask |= Power.BUTTON_LIGHT;
+ } else {
+ onMask |= Power.BUTTON_LIGHT;
+ }
+ }
+ }
+
+ if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
+ if (ANIMATE_SCREEN_LIGHTS) {
+ 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)Power.BRIGHTNESS_DIM)/preferredBrightness);
+ if (ratio > 1.0f) ratio = 1.0f;
+ if ((newState & SCREEN_ON_BIT) == 0) {
+ int steps;
+ if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
+ // was bright
+ steps = ANIM_STEPS;
+ } else {
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio*scale);
+ }
+ mScreenBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
+ steps, INITIAL_SCREEN_BRIGHTNESS);
+ } else {
+ int steps;
+ if ((oldState & SCREEN_ON_BIT) != 0) {
+ // was bright
+ steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
+ } else {
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio);
+ }
+ if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
+ // 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();
+ }
+ mScreenBrightness.setTargetLocked(Power.BRIGHTNESS_DIM,
+ steps, INITIAL_SCREEN_BRIGHTNESS);
+ }
+ } else {
+ mScreenBrightness.setTargetLocked(preferredBrightness,
+ ANIM_STEPS, INITIAL_SCREEN_BRIGHTNESS);
+ }
+ startAnimation = true;
+ } else {
+ if ((newState & SCREEN_BRIGHT_BIT) == 0) {
+ // dim or turn off backlight, depending on if the screen is on
+ if ((newState & SCREEN_ON_BIT) == 0) {
+ offMask |= Power.SCREEN_LIGHT;
+ } else {
+ dimMask |= Power.SCREEN_LIGHT;
+ }
+ } else {
+ onMask |= Power.SCREEN_LIGHT;
+ }
+ }
+ }
+
+ if (startAnimation) {
+ if (mSpew) {
+ Log.i(TAG, "Scheduling light animator!");
+ }
+ mHandler.removeCallbacks(mLightAnimator);
+ mHandler.post(mLightAnimator);
+ }
+
+ int err = 0;
+ if (offMask != 0) {
+ //Log.i(TAG, "Setting brightess off: " + offMask);
+ err |= Power.setLightBrightness(offMask, Power.BRIGHTNESS_OFF);
+ }
+ if (dimMask != 0) {
+ int brightness = Power.BRIGHTNESS_DIM;
+ if ((newState & BATTERY_LOW_BIT) != 0 &&
+ brightness > Power.BRIGHTNESS_LOW_BATTERY) {
+ brightness = Power.BRIGHTNESS_LOW_BATTERY;
+ }
+ //Log.i(TAG, "Setting brightess dim " + brightness + ": " + offMask);
+ err |= Power.setLightBrightness(dimMask, brightness);
+ }
+ if (onMask != 0) {
+ int brightness = getPreferredBrightness();
+ if ((newState & BATTERY_LOW_BIT) != 0 &&
+ brightness > Power.BRIGHTNESS_LOW_BATTERY) {
+ brightness = Power.BRIGHTNESS_LOW_BATTERY;
+ }
+ //Log.i(TAG, "Setting brightess on " + brightness + ": " + onMask);
+ err |= Power.setLightBrightness(onMask, brightness);
+ }
+
+ return err;
+ }
+
+ class BrightnessState {
+ final int mask;
+
+ boolean initialized;
+ int targetValue;
+ float curValue;
+ float delta;
+ boolean animating;
+
+ BrightnessState(int m) {
+ mask = m;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "animating=" + animating
+ + " targetValue=" + targetValue
+ + " curValue=" + curValue
+ + " delta=" + delta);
+ }
+
+ void setTargetLocked(int target, int stepsToTarget, int initialValue) {
+ if (!initialized) {
+ initialized = true;
+ curValue = (float)initialValue;
+ }
+ targetValue = target;
+ delta = (targetValue-curValue) / stepsToTarget;
+ if (mSpew) {
+ Log.i(TAG, "Setting target " + mask + ": cur=" + curValue
+ + " target=" + targetValue + " delta=" + delta);
+ }
+ animating = true;
+ }
+
+ boolean stepLocked() {
+ if (!animating) return false;
+ if (false && mSpew) {
+ Log.i(TAG, "Step target " + mask + ": cur=" + curValue
+ + " target=" + targetValue + " delta=" + delta);
+ }
+ curValue += delta;
+ int curIntValue = (int)curValue;
+ boolean more = true;
+ if (delta == 0) {
+ more = false;
+ } else if (delta > 0) {
+ if (curIntValue >= targetValue) {
+ curValue = curIntValue = targetValue;
+ more = false;
+ }
+ } else {
+ if (curIntValue <= targetValue) {
+ curValue = curIntValue = targetValue;
+ more = false;
+ }
+ }
+ //Log.i(TAG, "Animating brightess " + curIntValue + ": " + mask);
+ Power.setLightBrightness(mask, curIntValue);
+ animating = more;
+ if (!more) {
+ if (mask == Power.SCREEN_LIGHT && curIntValue == Power.BRIGHTNESS_OFF) {
+ screenOffFinishedAnimating(mOffBecauseOfUser);
+ }
+ }
+ return more;
+ }
+ }
+
+ private class LightAnimator implements Runnable {
+ public void run() {
+ synchronized (mLocks) {
+ long now = SystemClock.uptimeMillis();
+ boolean more = mScreenBrightness.stepLocked();
+ if (mKeyboardBrightness.stepLocked()) {
+ more = true;
+ }
+ if (mButtonBrightness.stepLocked()) {
+ more = true;
+ }
+ if (more) {
+ mHandler.postAtTime(mLightAnimator, now+(1000/60));
+ }
+ }
+ }
+ }
+
+ private int getPreferredBrightness() {
+ try {
+ if (mScreenBrightnessOverride >= 0) {
+ return mScreenBrightnessOverride;
+ }
+ final int brightness = Settings.System.getInt(mContext.getContentResolver(),
+ SCREEN_BRIGHTNESS);
+ // Don't let applications turn the screen all the way off
+ return Math.max(brightness, Power.BRIGHTNESS_DIM);
+ } catch (SettingNotFoundException snfe) {
+ return Power.BRIGHTNESS_ON;
+ }
+ }
+
+ boolean screenIsOn() {
+ synchronized (mLocks) {
+ return (mPowerState & SCREEN_ON_BIT) != 0;
+ }
+ }
+
+ boolean screenIsBright() {
+ synchronized (mLocks) {
+ return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT;
+ }
+ }
+
+ public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ userActivity(time, noChangeLights, OTHER_EVENT, force);
+ }
+
+ public void userActivity(long time, boolean noChangeLights) {
+ userActivity(time, noChangeLights, OTHER_EVENT, false);
+ }
+
+ public void userActivity(long time, boolean noChangeLights, int eventType) {
+ userActivity(time, noChangeLights, eventType, false);
+ }
+
+ public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
+ //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
+ && !((eventType == OTHER_EVENT) || (eventType == BUTTON_EVENT))) {
+ if (false) {
+ Log.d(TAG, "dropping mPokey=0x" + Integer.toHexString(mPokey));
+ }
+ return;
+ }
+
+ if (false) {
+ if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)) {
+ Log.d(TAG, "userActivity !!!");//, new RuntimeException());
+ } else {
+ Log.d(TAG, "mPokey=0x" + Integer.toHexString(mPokey));
+ }
+ }
+
+ synchronized (mLocks) {
+ if (mSpew) {
+ Log.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
+ + " mUserActivityAllowed=" + mUserActivityAllowed
+ + " mUserState=0x" + Integer.toHexString(mUserState)
+ + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ }
+ if (mLastEventTime <= time || force) {
+ mLastEventTime = time;
+ if (mUserActivityAllowed || force) {
+ // Only turn on button backlights if a button was pressed.
+ if (eventType == BUTTON_EVENT) {
+ mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
+ } else {
+ // don't clear button/keyboard backlights when the screen is touched.
+ mUserState |= SCREEN_BRIGHT;
+ }
+
+ reactivateWakeLocksLocked();
+ mWakeLockState = mLocks.gatherState();
+ setPowerState(mUserState | mWakeLockState, noChangeLights, true);
+ setTimeoutLocked(time, SCREEN_BRIGHT);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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)
+ {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ synchronized (mLocks) {
+ goToSleepLocked(time);
+ }
+ }
+
+ /**
+ * Returns the time the screen has been on since boot, in millis.
+ * @return screen on time
+ */
+ public long getScreenOnTime() {
+ synchronized (mLocks) {
+ if (mScreenOnStartTime == 0) {
+ return mScreenOnTime;
+ } else {
+ return SystemClock.elapsedRealtime() - mScreenOnStartTime + mScreenOnTime;
+ }
+ }
+ }
+
+ private void goToSleepLocked(long time) {
+
+ if (mLastEventTime <= time) {
+ mLastEventTime = time;
+ // cancel all of the wake locks
+ mWakeLockState = SCREEN_OFF;
+ int N = mLocks.size();
+ int numCleared = 0;
+ for (int i=0; i<N; i++) {
+ WakeLock wl = mLocks.get(i);
+ if (isScreenLock(wl.flags)) {
+ mLocks.get(i).activated = false;
+ numCleared++;
+ }
+ }
+ EventLog.writeEvent(LOG_POWER_SLEEP_REQUESTED, numCleared);
+ mUserState = SCREEN_OFF;
+ setPowerState(SCREEN_OFF, false, true);
+ cancelTimerLocked();
+ }
+ }
+
+ public long timeSinceScreenOn() {
+ synchronized (mLocks) {
+ if ((mPowerState & SCREEN_ON_BIT) != 0) {
+ return 0;
+ }
+ return SystemClock.elapsedRealtime() - mScreenOffTime;
+ }
+ }
+
+ public void setKeyboardVisibility(boolean visible) {
+ mKeyboardVisible = visible;
+ }
+
+ /**
+ * When the keyguard is up, it manages the power state, and userActivity doesn't do anything.
+ */
+ public void enableUserActivity(boolean enabled) {
+ synchronized (mLocks) {
+ mUserActivityAllowed = enabled;
+ mLastEventTime = SystemClock.uptimeMillis(); // we might need to pass this in
+ }
+ }
+
+ /** Sets the screen off timeouts:
+ * mKeylightDelay
+ * mDimDelay
+ * mScreenOffDelay
+ * */
+ private void setScreenOffTimeoutsLocked() {
+ if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) {
+ mKeylightDelay = mShortKeylightDelay; // Configurable via Gservices
+ mDimDelay = -1;
+ mScreenOffDelay = 0;
+ } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) {
+ mKeylightDelay = MEDIUM_KEYLIGHT_DELAY;
+ mDimDelay = -1;
+ mScreenOffDelay = 0;
+ } else {
+ int totalDelay = mTotalDelaySetting;
+ mKeylightDelay = LONG_KEYLIGHT_DELAY;
+ if (totalDelay < 0) {
+ mScreenOffDelay = Integer.MAX_VALUE;
+ } 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;
+ }
+ }
+ if (mSpew) {
+ Log.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay
+ + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay
+ + " mDimScreen=" + mDimScreen);
+ }
+ }
+
+ /**
+ * Refreshes cached Gservices settings. Called once on startup, and
+ * on subsequent Settings.Gservices.CHANGED_ACTION broadcasts (see
+ * GservicesChangedReceiver).
+ */
+ private void updateGservicesValues() {
+ mShortKeylightDelay = Settings.Gservices.getInt(
+ mContext.getContentResolver(),
+ Settings.Gservices.SHORT_KEYLIGHT_DELAY_MS,
+ SHORT_KEYLIGHT_DELAY_DEFAULT);
+ // Log.i(TAG, "updateGservicesValues(): mShortKeylightDelay now " + mShortKeylightDelay);
+ }
+
+ /**
+ * Receiver for the Gservices.CHANGED_ACTION broadcast intent,
+ * which tells us we need to refresh our cached Gservices settings.
+ */
+ private class GservicesChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Log.i(TAG, "GservicesChangedReceiver.onReceive(): " + intent);
+ updateGservicesValues();
+ }
+ }
+
+ private class LockList extends ArrayList<WakeLock>
+ {
+ void addLock(WakeLock wl)
+ {
+ int index = getIndex(wl.binder);
+ if (index < 0) {
+ this.add(wl);
+ }
+ }
+
+ WakeLock removeLock(IBinder binder)
+ {
+ int index = getIndex(binder);
+ if (index >= 0) {
+ return this.remove(index);
+ } else {
+ return null;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ void setPolicy(WindowManagerPolicy p) {
+ synchronized (mLocks) {
+ mPolicy = p;
+ mLocks.notifyAll();
+ }
+ }
+
+ WindowManagerPolicy getPolicyLocked() {
+ while (mPolicy == null || !mDoneBooting) {
+ try {
+ mLocks.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ return mPolicy;
+ }
+
+ void systemReady() {
+ synchronized (mLocks) {
+ Log.d(TAG, "system ready!");
+ mDoneBooting = true;
+ userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
+ updateWakeLockLocked();
+ mLocks.notifyAll();
+ }
+ }
+
+ public void monitor() {
+ synchronized (mLocks) { }
+ }
+}