Michael Chan e96440fa15 AI 148368: Decrease CPU usage by throttling touch events
We are decreasing CPU usage at the cost of event latency. Events are queued up
  and released to the app at the specified rate. You can
  experiment with different values via:
  adb shell setprop windowsmgr.max_events_per_sec 35
  The new value is picked up when you let go and retouch the screen. No
  reboot needed.
  Also the following changes were made after profiling:
  - In WindowManagerService, limit the call to userActivity() when we
  have a flood touch events.
  - In PowerManagerService, skip checking of permission if the caller
  is system user.
  - In PowerManagerService, integrated the functionality of gatherState()
  into reactivateWakeLocksLocked(). They loop through the same data
  structure and are called back to back.
  BUG=1692771

Automated import of CL 148368
2009-05-06 10:27:36 -07:00

1997 lines
78 KiB
Java

/*
* 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[] mBroadcastQueue = new int[] { -1, -1, -1 };
private int[] mBroadcastWhy = new int[3];
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 HardwareService mHardware;
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(SCREEN_BRIGHT_BIT);
private final BrightnessState mKeyboardBrightness
= new BrightnessState(KEYBOARD_BRIGHT_BIT);
private final BrightnessState mButtonBrightness
= new BrightnessState(BUTTON_BRIGHT_BIT);
private boolean mStillNeedSleepNotification;
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, HardwareService hardware, IActivityManager activity,
BatteryService battery) {
mHardware = hardware;
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_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);
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) {
int uid = Binder.getCallingUid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
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) {
int oldWakeLockState = mWakeLockState;
mWakeLockState = mLocks.reactivateScreenLocksLocked();
if (mSpew) {
Log.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
+ " mWakeLockState=0x"
+ Integer.toHexString(mWakeLockState)
+ " previous wakeLockState=0x" + Integer.toHexString(oldWakeLockState));
}
} 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) {
int uid = Binder.getCallingUid();
if (uid != Process.myUid()) {
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 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(" 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);
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_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0
? " POKE_LOCK_IGNORE_TOUCH_AND_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) {
mStillNeedSleepNotification = false;
}
// Add to the queue.
int index = 0;
while (mBroadcastQueue[index] != -1) {
index++;
}
mBroadcastQueue[index] = on ? 1 : 0;
mBroadcastWhy[index] = why;
// 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) {
// Also, while we're collapsing them, if it's going to be an "off," and one
// is off because of user, then use that, regardless of whether it's the first
// or second one.
if (!on && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
mBroadcastWhy[0] = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
}
mBroadcastQueue[0] = on ? 1 : 0;
mBroadcastQueue[1] = -1;
mBroadcastQueue[2] = -1;
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(LOG_POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
mBroadcastWakeLock.release();
}
// 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(LOG_POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount);
mHandler.post(mNotificationTask);
}
}
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) {
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.
break;
}
}
}
};
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) {
updateLightsLocked(newState, 0);
mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
}
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 (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.noteScreenBrightness(
getPreferredBrightness());
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 = screenOffFinishedAnimatingLocked(becauseOfUser);
} else {
mOffBecauseOfUser = becauseOfUser;
err = 0;
mLastTouchDown = 0;
}
}
}
}
}
private int screenOffFinishedAnimatingLocked(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 void updateLightsLocked(int newState, int forceState) {
final int oldState = mPowerState;
final int realDifference = (newState ^ oldState);
final int difference = realDifference | forceState;
if (difference == 0) {
return;
}
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,
preferredBrightness);
} else {
mKeyboardBrightness.setTargetLocked(preferredBrightness,
ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
Power.BRIGHTNESS_OFF);
}
startAnimation = true;
} else {
if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
offMask |= KEYBOARD_BRIGHT_BIT;
} else {
onMask |= KEYBOARD_BRIGHT_BIT;
}
}
}
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,
preferredBrightness);
} else {
mButtonBrightness.setTargetLocked(preferredBrightness,
ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
Power.BRIGHTNESS_OFF);
}
startAnimation = true;
} else {
if ((newState & BUTTON_BRIGHT_BIT) == 0) {
offMask |= BUTTON_BRIGHT_BIT;
} else {
onMask |= BUTTON_BRIGHT_BIT;
}
}
}
if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
if (ANIMATE_SCREEN_LIGHTS) {
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 = Power.BRIGHTNESS_DIM;
break;
case 0:
nominalCurrentValue = Power.BRIGHTNESS_OFF;
break;
case SCREEN_BRIGHT_BIT:
default:
// not possible
nominalCurrentValue = (int)mScreenBrightness.curValue;
break;
}
}
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)Power.BRIGHTNESS_DIM)/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 = Power.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);
}
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();
}
brightness = Power.BRIGHTNESS_DIM;
}
}
long identity = Binder.clearCallingIdentity();
try {
mBatteryStats.noteScreenBrightness(brightness);
} catch (RemoteException e) {
// Nothing interesting to do.
} finally {
Binder.restoreCallingIdentity(identity);
}
mScreenBrightness.setTargetLocked(brightness,
steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
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 |= SCREEN_BRIGHT_BIT;
} else {
dimMask |= SCREEN_BRIGHT_BIT;
}
} else {
onMask |= SCREEN_BRIGHT_BIT;
}
}
}
if (startAnimation) {
if (mSpew) {
Log.i(TAG, "Scheduling light animator!");
}
mHandler.removeCallbacks(mLightAnimator);
mHandler.post(mLightAnimator);
}
if (offMask != 0) {
//Log.i(TAG, "Setting brightess off: " + offMask);
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);
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);
setLightBrightness(onMask, brightness);
}
}
private void setLightBrightness(int mask, int value) {
if ((mask & SCREEN_BRIGHT_BIT) != 0) {
mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value);
}
if ((mask & BUTTON_BRIGHT_BIT) != 0) {
mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value);
}
if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value);
}
}
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,
int nominalCurrentValue) {
if (!initialized) {
initialized = true;
curValue = (float)initialValue;
}
targetValue = target;
delta = (targetValue -
(nominalCurrentValue >= 0 ? nominalCurrentValue : curValue))
/ stepsToTarget;
if (mSpew) {
String noticeMe = nominalCurrentValue == curValue ? "" : " ******************";
Log.i(TAG, "Setting target " + mask + ": cur=" + curValue
+ " target=" + targetValue + " delta=" + delta
+ " nominalCurrentValue=" + nominalCurrentValue
+ noticeMe);
}
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) {
curValue = curIntValue = targetValue;
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);
setLightBrightness(mask, curIntValue);
animating = more;
if (!more) {
if (mask == SCREEN_BRIGHT_BIT && curIntValue == Power.BRIGHTNESS_OFF) {
screenOffFinishedAnimatingLocked(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 == CHEEK_EVENT || eventType == TOUCH_EVENT)) {
if (false) {
Log.d(TAG, "dropping cheek or short event mPokey=0x" + Integer.toHexString(mPokey));
}
return;
}
if (((mPokey & POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0)
&& (eventType == TOUCH_EVENT || eventType == TOUCH_UP_EVENT
|| eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT)) {
if (false) {
Log.d(TAG, "dropping touch 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;
}
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, 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);
mStillNeedSleepNotification = true;
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;
}
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;
}
}
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;
long identity = Binder.clearCallingIdentity();
try {
mBatteryStats.noteScreenBrightness(getPreferredBrightness());
mBatteryStats.noteScreenOn();
} catch (RemoteException e) {
// Nothing interesting to do.
} finally {
Binder.restoreCallingIdentity(identity);
}
userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
updateWakeLockLocked();
mLocks.notifyAll();
}
}
public void monitor() {
synchronized (mLocks) { }
}
}