Files
frameworks_base/services/java/com/android/server/power/PowerManagerService.java
John Spurlock 10fb224623 Disable screensavers if below first battery warning level.
Don't activate on sleep if below the threshold, and quit any currently
running dream when the threshold is reached.

Bug:6999949
Change-Id: I961b350d24ee6f95e502228aaa57312b0ffbadc1
2012-08-23 16:23:18 -04:00

2113 lines
81 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.power;
import com.android.internal.app.IBatteryStats;
import com.android.server.BatteryService;
import com.android.server.EventLogTags;
import com.android.server.LightsService;
import com.android.server.TwilightService;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
import com.android.server.display.DisplayManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.WindowManagerPolicy;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import libcore.util.Objects;
/**
* The power manager service is responsible for coordinating power management
* functions on the device.
*/
public final class PowerManagerService extends IPowerManager.Stub
implements Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
// Message: Sent when a user activity timeout occurs to update the power state.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a napping or dreaming state.
private static final int MSG_SANDMAN = 2;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
private static final int DIRTY_WAKEFULNESS = 1 << 1;
// Dirty bit: user activity was poked or may have timed out
private static final int DIRTY_USER_ACTIVITY = 1 << 2;
// Dirty bit: actual display power state was updated asynchronously
private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3;
// Dirty bit: mBootCompleted changed
private static final int DIRTY_BOOT_COMPLETED = 1 << 4;
// Dirty bit: settings changed
private static final int DIRTY_SETTINGS = 1 << 5;
// Dirty bit: mIsPowered changed
private static final int DIRTY_IS_POWERED = 1 << 6;
// Dirty bit: mStayOn changed
private static final int DIRTY_STAY_ON = 1 << 7;
// Dirty bit: battery state changed
private static final int DIRTY_BATTERY_STATE = 1 << 8;
// Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
// The screen should be off or in the process of being turned off by the display controller.
private static final int WAKEFULNESS_ASLEEP = 0;
// Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep().
// When the user activity timeout expires, the device may start napping.
private static final int WAKEFULNESS_AWAKE = 1;
// Wakefulness: The device is napping. It is deciding whether to dream or go to sleep
// but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which
// ends the nap. User activity may brighten the screen but does not end the nap.
private static final int WAKEFULNESS_NAPPING = 2;
// Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(),
// which ends the dream. The device goes to sleep when goToSleep() is called, when
// the dream ends or when unplugged.
// User activity may brighten the screen but does not end the dream.
private static final int WAKEFULNESS_DREAMING = 3;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;
private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
// Summarizes the user activity state.
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;
// Default and minimum screen off timeout in milliseconds.
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000;
// The screen dim duration, in seconds.
// This is subtracted from the end of the screen off timeout so the
// minimum screen off timeout should be longer than this.
private static final int SCREEN_DIM_DURATION = 7 * 1000;
private Context mContext;
private LightsService mLightsService;
private BatteryService mBatteryService;
private IBatteryStats mBatteryStats;
private HandlerThread mHandlerThread;
private PowerManagerHandler mHandler;
private WindowManagerPolicy mPolicy;
private Notifier mNotifier;
private DisplayPowerController mDisplayPowerController;
private SettingsObserver mSettingsObserver;
private IDreamManager mDreamManager;
private LightsService.Light mAttentionLight;
private final Object mLock = new Object();
// A bitfield that indicates what parts of the power state have
// changed and need to be recalculated.
private int mDirty;
// Indicates whether the device is awake or asleep or somewhere in between.
// This is distinct from the screen power state, which is managed separately.
private int mWakefulness;
// True if MSG_SANDMAN has been scheduled.
private boolean mSandmanScheduled;
// Table of all suspend blockers.
// There should only be a few of these.
private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
// Table of all wake locks acquired by applications.
private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
// A bitfield that summarizes the state of all active wakelocks.
private int mWakeLockSummary;
// If true, instructs the display controller to wait for the proximity sensor to
// go negative before turning the screen on.
private boolean mRequestWaitForNegativeProximity;
// Timestamp of the last time the device was awoken or put to sleep.
private long mLastWakeTime;
private long mLastSleepTime;
// True if we need to send a wake up or go to sleep finished notification
// when the display is ready.
private boolean mSendWakeUpFinishedNotificationWhenReady;
private boolean mSendGoToSleepFinishedNotificationWhenReady;
// Timestamp of the last call to user activity.
private long mLastUserActivityTime;
private long mLastUserActivityTimeNoChangeLights;
// A bitfield that summarizes the effect of the user activity timer.
// A zero value indicates that the user activity timer has expired.
private int mUserActivitySummary;
// The desired display power state. The actual state may lag behind the
// requested because it is updated asynchronously by the display power controller.
private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
// The time the screen was last turned off, in elapsedRealtime() timebase.
private long mLastScreenOffEventElapsedRealTime;
// True if the display power state has been fully applied, which means the display
// is actually on or actually off or whatever was requested.
private boolean mDisplayReady;
// True if holding a wake-lock to block suspend of the CPU.
private boolean mHoldingWakeLockSuspendBlocker;
// The suspend blocker used to keep the CPU alive when wake locks have been acquired.
private final SuspendBlocker mWakeLockSuspendBlocker;
// True if systemReady() has been called.
private boolean mSystemReady;
// True if boot completed occurred. We keep the screen on until this happens.
private boolean mBootCompleted;
// True if the device is plugged into a power source.
private boolean mIsPowered;
// True if the device should wake up when plugged or unplugged.
private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
// True if dreams are supported on this device.
private boolean mDreamsSupportedConfig;
// True if dreams are enabled by the user.
private boolean mDreamsEnabledSetting;
// True if dreams should be activated on sleep.
private boolean mDreamsActivateOnSleepSetting;
// The screen off timeout setting value in milliseconds.
private int mScreenOffTimeoutSetting;
// The maximum allowable screen off timeout according to the device
// administration policy. Overrides other settings.
private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
// The stay on while plugged in setting.
// A bitfield of battery conditions under which to make the screen stay on.
private int mStayOnWhilePluggedInSetting;
// True if the device should stay on.
private boolean mStayOn;
// Screen brightness setting limits.
private int mScreenBrightnessSettingMinimum;
private int mScreenBrightnessSettingMaximum;
private int mScreenBrightnessSettingDefault;
// The screen brightness setting, from 0 to 255.
// Use -1 if no value has been set.
private int mScreenBrightnessSetting;
// The screen auto-brightness adjustment setting, from -1 to 1.
// Use 0 if there is no adjustment.
private float mScreenAutoBrightnessAdjustmentSetting;
// The screen brightness mode.
// One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
private int mScreenBrightnessModeSetting;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
private int mScreenBrightnessOverrideFromWindowManager = -1;
// The screen brightness setting override from the settings application
// to temporarily adjust the brightness until next updated,
// Use -1 to disable.
private int mTemporaryScreenBrightnessSettingOverride = -1;
// The screen brightness adjustment setting override from the settings
// application to temporarily adjust the auto-brightness adjustment factor
// until next updated, in the range -1..1.
// Use NaN to disable.
private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
private native void nativeInit();
private static native void nativeShutdown();
private static native void nativeReboot(String reason) throws IOException;
private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
static native void nativeSetScreenState(boolean on);
public PowerManagerService() {
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
mWakeLockSuspendBlocker.acquire();
mHoldingWakeLockSuspendBlocker = true;
mWakefulness = WAKEFULNESS_AWAKE;
}
nativeInit();
}
/**
* Initialize the power manager.
* Must be called before any other functions within the power manager are called.
*/
public void init(Context context, LightsService ls,
ActivityManagerService am, BatteryService bs, IBatteryStats bss,
DisplayManagerService dm) {
mContext = context;
mLightsService = ls;
mBatteryService = bs;
mBatteryStats = bss;
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addMonitor(this);
}
public void setPolicy(WindowManagerPolicy policy) {
synchronized (mLock) {
mPolicy = policy;
}
}
public void systemReady(TwilightService twilight) {
synchronized (mLock) {
mSystemReady = true;
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats,
createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mPolicy, mScreenOnListener);
mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
mContext, mNotifier, mLightsService, twilight,
createSuspendBlockerLocked("PowerManagerService.Display"),
mDisplayPowerControllerCallbacks, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
// Register for broadcasts from other components of the system.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new BatteryReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(new BootCompletedReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(), filter);
// Register for settings changes.
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ENABLED), false, mSettingsObserver);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), false, mSettingsObserver);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver);
// Go.
readConfigurationLocked();
updateSettingsLocked();
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
}
}
private void readConfigurationLocked() {
final Resources resources = mContext.getResources();
mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);
mDreamsSupportedConfig = resources.getBoolean(
com.android.internal.R.bool.config_enableDreams);
}
private void updateSettingsLocked() {
final ContentResolver resolver = mContext.getContentResolver();
mDreamsEnabledSetting = (Settings.Secure.getInt(resolver,
Settings.Secure.SCREENSAVER_ENABLED, 0) != 0);
mDreamsActivateOnSleepSetting = (Settings.Secure.getInt(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0) != 0);
mScreenOffTimeoutSetting = Settings.System.getInt(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
mStayOnWhilePluggedInSetting = Settings.System.getInt(resolver,
Settings.System.STAY_ON_WHILE_PLUGGED_IN,
BatteryManager.BATTERY_PLUGGED_AC);
final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
mScreenBrightnessSetting = Settings.System.getInt(resolver,
Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault);
if (oldScreenBrightnessSetting != mScreenBrightnessSetting) {
mTemporaryScreenBrightnessSettingOverride = -1;
}
final float oldScreenAutoBrightnessAdjustmentSetting =
mScreenAutoBrightnessAdjustmentSetting;
mScreenAutoBrightnessAdjustmentSetting = Settings.System.getFloat(resolver,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f);
if (oldScreenAutoBrightnessAdjustmentSetting != mScreenAutoBrightnessAdjustmentSetting) {
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
}
mScreenBrightnessModeSetting = Settings.System.getInt(resolver,
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
mDirty |= DIRTY_SETTINGS;
}
private void handleSettingsChangedLocked() {
updateSettingsLocked();
updatePowerStateLocked();
}
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
PowerManager.validateWakeLockParameters(flags, tag);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (ws != null && ws.size() != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.UPDATE_DEVICE_STATS, null);
} else {
ws = null;
}
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, flags, tag, ws, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
int uid, int pid) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
+ ", flags=0x" + Integer.toHexString(flags)
+ ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid);
}
WakeLock wakeLock;
int index = findWakeLockIndexLocked(lock);
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.updateProperties(flags, tag, ws, uid, pid);
notifyWakeLockAcquiredLocked(wakeLock);
}
} else {
wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
throw new IllegalArgumentException("Wake lock is already dead.");
}
notifyWakeLockAcquiredLocked(wakeLock);
mWakeLocks.add(wakeLock);
}
applyWakeLockFlagsOnAcquireLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
}
}
@Override // Binder call
public void releaseWakeLock(IBinder lock, int flags) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
final long ident = Binder.clearCallingIdentity();
try {
releaseWakeLockInternal(lock, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void releaseWakeLockInternal(IBinder lock, int flags) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
+ ", flags=0x" + Integer.toHexString(flags));
}
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
return;
}
WakeLock wakeLock = mWakeLocks.get(index);
mWakeLocks.remove(index);
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.mLock.unlinkToDeath(wakeLock, 0);
if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) {
mRequestWaitForNegativeProximity = true;
}
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private void handleWakeLockDeath(WakeLock wakeLock) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock));
}
int index = mWakeLocks.indexOf(wakeLock);
if (index < 0) {
return;
}
mWakeLocks.remove(index);
notifyWakeLockReleasedLocked(wakeLock);
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
wakeLock.mOwnerUid);
}
}
@Override // Binder call
public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (ws != null && ws.size() != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.UPDATE_DEVICE_STATS, null);
} else {
ws = null;
}
final long ident = Binder.clearCallingIdentity();
try {
updateWakeLockWorkSourceInternal(lock, ws);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) {
synchronized (mLock) {
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
throw new IllegalArgumentException("Wake lock not active");
}
WakeLock wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameWorkSource(ws)) {
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.updateWorkSource(ws);
notifyWakeLockAcquiredLocked(wakeLock);
}
}
}
private int findWakeLockIndexLocked(IBinder lock) {
final int count = mWakeLocks.size();
for (int i = 0; i < count; i++) {
if (mWakeLocks.get(i).mLock == lock) {
return i;
}
}
return -1;
}
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady) {
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
if (mSystemReady) {
mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
@Override // Binder call
public boolean isWakeLockLevelSupported(int level) {
final long ident = Binder.clearCallingIdentity();
try {
return isWakeLockLevelSupportedInternal(level);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isWakeLockLevelSupportedInternal(int level) {
synchronized (mLock) {
switch (level) {
case PowerManager.PARTIAL_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.FULL_WAKE_LOCK:
return true;
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
return mSystemReady && mDisplayPowerController.isProximitySensorAvailable();
default:
return false;
}
}
}
@Override // Binder call
public void userActivity(long eventTime, int event, int flags) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
userActivityInternal(eventTime, event, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void userActivityFromNative(long eventTime, int event, int flags) {
userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
}
private void userActivityInternal(long eventTime, int event, int flags, int uid) {
synchronized (mLock) {
if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
updatePowerStateLocked();
}
}
}
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
+ ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
+ ", uid=" + uid);
}
if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
|| mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) {
return false;
}
mNotifier.onUserActivity(event, uid);
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
if (eventTime > mLastUserActivityTimeNoChangeLights
&& eventTime > mLastUserActivityTime) {
mLastUserActivityTimeNoChangeLights = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
return true;
}
} else {
if (eventTime > mLastUserActivityTime) {
mLastUserActivityTime = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
return true;
}
}
return false;
}
@Override // Binder call
public void wakeUp(long eventTime) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
wakeUpInternal(eventTime);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void wakeUpFromNative(long eventTime) {
wakeUpInternal(eventTime);
}
private void wakeUpInternal(long eventTime) {
synchronized (mLock) {
if (wakeUpNoUpdateLocked(eventTime)) {
updatePowerStateLocked();
}
}
}
private boolean wakeUpNoUpdateLocked(long eventTime) {
if (DEBUG_SPEW) {
Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime);
}
if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}
switch (mWakefulness) {
case WAKEFULNESS_ASLEEP:
Slog.i(TAG, "Waking up from sleep...");
mNotifier.onWakeUpStarted();
mSendWakeUpFinishedNotificationWhenReady = true;
mSendGoToSleepFinishedNotificationWhenReady = false;
break;
case WAKEFULNESS_DREAMING:
Slog.i(TAG, "Waking up from dream...");
break;
case WAKEFULNESS_NAPPING:
Slog.i(TAG, "Waking up from nap...");
break;
}
mLastWakeTime = eventTime;
mWakefulness = WAKEFULNESS_AWAKE;
mDirty |= DIRTY_WAKEFULNESS;
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
return true;
}
@Override // Binder call
public void goToSleep(long eventTime, int reason) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
goToSleepInternal(eventTime, reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void goToSleepFromNative(long eventTime, int reason) {
goToSleepInternal(eventTime, reason);
}
private void goToSleepInternal(long eventTime, int reason) {
synchronized (mLock) {
if (goToSleepNoUpdateLocked(eventTime, reason)) {
updatePowerStateLocked();
}
}
}
private boolean goToSleepNoUpdateLocked(long eventTime, int reason) {
if (DEBUG_SPEW) {
Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason);
}
if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
|| !mBootCompleted || !mSystemReady) {
return false;
}
switch (reason) {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
Slog.i(TAG, "Going to sleep due to device administration policy...");
break;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
Slog.i(TAG, "Going to sleep due to screen timeout...");
break;
default:
Slog.i(TAG, "Going to sleep by user request...");
reason = PowerManager.GO_TO_SLEEP_REASON_USER;
break;
}
mLastSleepTime = eventTime;
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_ASLEEP;
mNotifier.onGoToSleepStarted(reason);
mSendGoToSleepFinishedNotificationWhenReady = true;
mSendWakeUpFinishedNotificationWhenReady = false;
// Report the number of wake locks that will be cleared by going to sleep.
int numWakeLocksCleared = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
numWakeLocksCleared += 1;
break;
}
}
EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
return true;
}
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) {
return;
}
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update dreams and display power state.
updateDreamLocked(dirtyPhase2);
updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Send notifications, if needed.
sendPendingNotificationsLocked();
// Phase 4: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
}
private void sendPendingNotificationsLocked() {
if (mDisplayReady) {
if (mSendWakeUpFinishedNotificationWhenReady) {
mSendWakeUpFinishedNotificationWhenReady = false;
mNotifier.onWakeUpFinished();
}
if (mSendGoToSleepFinishedNotificationWhenReady) {
mSendGoToSleepFinishedNotificationWhenReady = false;
mNotifier.onGoToSleepFinished();
}
}
}
/**
* Updates the value of mIsPowered.
* Sets DIRTY_IS_POWERED if a change occurred.
*/
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
boolean wasPowered = mIsPowered;
mIsPowered = mBatteryService.isPowered();
if (wasPowered != mIsPowered) {
mDirty |= DIRTY_IS_POWERED;
// Treat plugging and unplugging the devices as a user activity.
// Users find it disconcerting when they plug or unplug the device
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
final long now = SystemClock.uptimeMillis();
if (mWakeUpWhenPluggedOrUnpluggedConfig) {
wakeUpNoUpdateLocked(now);
}
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
}
}
}
/**
* Updates the value of mStayOn.
* Sets DIRTY_STAY_ON if a change occurred.
*/
private void updateStayOnLocked(int dirty) {
if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
}
}
/**
* Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
* Note that most wake-locks are ignored when the system is asleep.
*
* This function must have no other side-effects.
*/
private void updateWakeLockSummaryLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
mWakeLockSummary = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.PARTIAL_WAKE_LOCK:
mWakeLockSummary |= WAKE_LOCK_CPU;
break;
case PowerManager.FULL_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU
| WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
}
break;
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
}
break;
case PowerManager.SCREEN_DIM_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
}
break;
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF;
}
break;
}
}
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
+ wakefulnessToString(mWakefulness)
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
}
}
}
/**
* Updates the value of mUserActivitySummary to summarize the user requested
* state of the system such as whether the screen should be bright or dim.
* Note that user activity is ignored when the system is asleep.
*
* This function must have no other side-effects.
*/
private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
long nextTimeout = 0;
if (mWakefulness != WAKEFULNESS_ASLEEP) {
final int screenOffTimeout = getScreenOffTimeoutLocked();
final int screenDimDuration = getScreenDimDurationLocked();
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
}
}
}
if (mUserActivitySummary == 0
&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < nextTimeout
&& mDisplayPowerRequest.screenState
!= DisplayPowerRequest.SCREEN_STATE_OFF) {
mUserActivitySummary = mDisplayPowerRequest.screenState
== DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
}
}
if (mUserActivitySummary != 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
}
} else {
mUserActivitySummary = 0;
}
if (DEBUG_SPEW) {
Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
+ wakefulnessToString(mWakefulness)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
}
}
}
/**
* Called when a user activity timeout has occurred.
* Simply indicates that something about user activity has changed so that the new
* state can be recomputed when the power state is updated.
*
* This function must have no other side-effects besides setting the dirty
* bit and calling update power state. Wakefulness transitions are handled elsewhere.
*/
private void handleUserActivityTimeout() { // runs on handler thread
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "handleUserActivityTimeout");
}
mDirty |= DIRTY_USER_ACTIVITY;
updatePowerStateLocked();
}
}
private int getScreenOffTimeoutLocked() {
int timeout = mScreenOffTimeoutSetting;
if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
}
return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT);
}
private int getScreenDimDurationLocked() {
return SCREEN_DIM_DURATION;
}
/**
* Updates the wakefulness of the device.
*
* This is the function that decides whether the device should start napping
* based on the current wake locks and user activity state. It may modify mDirty
* if the wakefulness changes.
*
* Returns true if the wakefulness changed and we need to restart power state calculation.
*/
private boolean updateWakefulnessLocked(int dirty) {
boolean changed = false;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) {
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Nap time...");
}
mWakefulness = WAKEFULNESS_NAPPING;
mDirty |= DIRTY_WAKEFULNESS;
changed = true;
}
}
return changed;
}
// Also used when exiting a dream to determine whether we should go back
// to being fully awake or else go to sleep for good.
private boolean isItBedTimeYetLocked() {
return mBootCompleted && !mStayOn
&& (mWakeLockSummary
& (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0
&& (mUserActivitySummary
& (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0;
}
/**
* Determines whether to post a message to the sandman to update the dream state.
*/
private void updateDreamLocked(int dirty) {
if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS
| DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_BATTERY_STATE)) != 0) {
scheduleSandmanLocked();
}
}
private void scheduleSandmanLocked() {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
/**
* Called when the device enters or exits a napping or dreaming state.
*
* We do this asynchronously because we must call out of the power manager to start
* the dream and we don't want to hold our lock while doing so. There is a risk that
* the device will wake or go to sleep in the meantime so we have to handle that case.
*/
private void handleSandman() { // runs on handler thread
// Handle preconditions.
boolean startDreaming = false;
synchronized (mLock) {
mSandmanScheduled = false;
boolean canDream = canDreamLocked();
if (DEBUG_SPEW) {
Log.d(TAG, "handleSandman: canDream=" + canDream
+ ", mWakefulness=" + wakefulnessToString(mWakefulness));
}
if (canDream && mWakefulness == WAKEFULNESS_NAPPING) {
startDreaming = true;
}
}
// Get the dream manager, if needed.
if (startDreaming && mDreamManager == null) {
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService("dreams"));
if (mDreamManager == null) {
Slog.w(TAG, "Unable to find IDreamManager.");
}
}
// Start dreaming if needed.
// We only control the dream on the handler thread, so we don't need to worry about
// concurrent attempts to start or stop the dream.
boolean isDreaming = false;
if (mDreamManager != null) {
try {
isDreaming = mDreamManager.isDreaming();
if (startDreaming && !isDreaming) {
Slog.i(TAG, "Entering dreamland.");
mDreamManager.dream();
isDreaming = mDreamManager.isDreaming();
if (!isDreaming) {
Slog.i(TAG, "Could not enter dreamland. Sleep will be dreamless.");
}
}
} catch (RemoteException ex) {
}
}
// Update dream state.
// We might need to stop the dream again if the preconditions changed.
boolean continueDreaming = false;
synchronized (mLock) {
if (isDreaming && canDreamLocked()) {
if (mWakefulness == WAKEFULNESS_NAPPING) {
mWakefulness = WAKEFULNESS_DREAMING;
mDirty |= DIRTY_WAKEFULNESS;
updatePowerStateLocked();
continueDreaming = true;
} else if (mWakefulness == WAKEFULNESS_DREAMING) {
continueDreaming = true;
}
}
if (!continueDreaming) {
handleDreamFinishedLocked();
}
// Allow the sandman to detect when the dream has ended.
// FIXME: The DreamManagerService should tell us explicitly.
if (mWakefulness == WAKEFULNESS_DREAMING
|| mWakefulness == WAKEFULNESS_NAPPING) {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, 1000);
}
}
}
// Stop dreaming if needed.
// It's possible that something else changed to make us need to start the dream again.
// If so, then the power manager will have posted another message to the handler
// to take care of it later.
if (mDreamManager != null) {
try {
if (!continueDreaming && isDreaming) {
Slog.i(TAG, "Leaving dreamland.");
mDreamManager.awaken();
}
} catch (RemoteException ex) {
}
}
}
/**
* Returns true if the device is allowed to dream in its current state,
* assuming there has been no recent user activity and no wake locks are held.
*/
private boolean canDreamLocked() {
return mIsPowered
&& mDreamsSupportedConfig
&& mDreamsEnabledSetting
&& mDreamsActivateOnSleepSetting
&& !mBatteryService.isBatteryLow();
}
/**
* Called when a dream is ending to figure out what to do next.
*/
private void handleDreamFinishedLocked() {
if (mWakefulness == WAKEFULNESS_NAPPING
|| mWakefulness == WAKEFULNESS_DREAMING) {
if (isItBedTimeYetLocked()) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
updatePowerStateLocked();
} else {
wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
updatePowerStateLocked();
}
}
}
/**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true. The display
* controller posts a message to tell us when the actual display power state
* has been updated so we come back here to double-check and finish up.
*
* This function recalculates the display power state each time.
*/
private void updateDisplayPowerStateLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS)) != 0) {
int newScreenState = getDesiredScreenPowerState();
if (newScreenState != mDisplayPowerRequest.screenState) {
if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
&& mDisplayPowerRequest.screenState
!= DisplayPowerRequest.SCREEN_STATE_OFF) {
mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
}
mDisplayPowerRequest.screenState = newScreenState;
nativeSetPowerState(
newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
}
int screenBrightness = mScreenBrightnessSettingDefault;
float screenAutoBrightnessAdjustment = 0.0f;
boolean autoBrightness = (mScreenBrightnessModeSetting ==
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
screenBrightness = mScreenBrightnessOverrideFromWindowManager;
autoBrightness = false;
} else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
screenBrightness = mTemporaryScreenBrightnessSettingOverride;
} else if (isValidBrightness(mScreenBrightnessSetting)) {
screenBrightness = mScreenBrightnessSetting;
}
if (autoBrightness) {
screenBrightness = mScreenBrightnessSettingDefault;
if (isValidAutoBrightnessAdjustment(
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
screenAutoBrightnessAdjustment =
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
} else if (isValidAutoBrightnessAdjustment(
mScreenAutoBrightnessAdjustmentSetting)) {
screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
}
}
screenBrightness = Math.max(Math.min(screenBrightness,
mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
screenAutoBrightnessAdjustment = Math.max(Math.min(
screenAutoBrightnessAdjustment, 1.0f), -1.0f);
mDisplayPowerRequest.screenBrightness = screenBrightness;
mDisplayPowerRequest.screenAutoBrightnessAdjustment =
screenAutoBrightnessAdjustment;
mDisplayPowerRequest.useAutoBrightness = autoBrightness;
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady
+ ", newScreenState=" + newScreenState
+ ", mWakefulness=" + mWakefulness
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", mBootCompleted=" + mBootCompleted);
}
}
}
private static boolean isValidBrightness(int value) {
return value >= 0 && value <= 255;
}
private static boolean isValidAutoBrightnessAdjustment(float value) {
// Handles NaN by always returning false.
return value >= -1.0f && value <= 1.0f;
}
private int getDesiredScreenPowerState() {
if (mWakefulness == WAKEFULNESS_ASLEEP) {
return DisplayPowerRequest.SCREEN_STATE_OFF;
}
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted) {
return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
}
return DisplayPowerRequest.SCREEN_STATE_DIM;
}
private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks =
new DisplayPowerController.Callbacks() {
@Override
public void onStateChanged() {
mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
updatePowerStateLocked();
}
@Override
public void onProximityNegative() {
userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
};
private boolean shouldUseProximitySensorLocked() {
return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
}
/**
* Updates the suspend blocker that keeps the CPU alive.
*
* This function must have no other side-effects.
*/
private void updateSuspendBlockerLocked() {
boolean wantCpu = isCpuNeededLocked();
if (wantCpu != mHoldingWakeLockSuspendBlocker) {
mHoldingWakeLockSuspendBlocker = wantCpu;
if (wantCpu) {
if (DEBUG) {
Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker.");
}
mWakeLockSuspendBlocker.acquire();
} else {
if (DEBUG) {
Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker.");
}
mWakeLockSuspendBlocker.release();
}
}
}
private boolean isCpuNeededLocked() {
return !mBootCompleted
|| mWakeLockSummary != 0
|| mUserActivitySummary != 0
|| mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
|| !mDisplayReady;
}
@Override // Binder call
public boolean isScreenOn() {
final long ident = Binder.clearCallingIdentity();
try {
return isScreenOnInternal();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isScreenOnInternal() {
synchronized (mLock) {
return !mSystemReady
|| mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
}
}
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
}
private void handleBootCompletedLocked() {
final long now = SystemClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
private void handleDockStateChangedLocked(int dockState) {
// TODO
}
/**
* Reboot the device immediately, passing 'reason' (may be null)
* to the underlying __reboot system call. Should not return.
*/
@Override // Binder call
public void reboot(String reason) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
rebootInternal(reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void rebootInternal(final String reason) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call reboot()");
}
Runnable runnable = new Runnable() {
public void run() {
synchronized (this) {
ShutdownThread.reboot(mContext, reason, false);
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(mHandler, runnable);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
/**
* Crash the runtime (causing a complete restart of the Android framework).
* Requires REBOOT permission. Mostly for testing. Should not return.
*/
@Override // Binder call
public void crash(String message) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
crashInternal(message);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void crashInternal(final String message) {
Thread t = new Thread("PowerManagerService.crash()") {
public void run() {
throw new RuntimeException(message);
}
};
try {
t.start();
t.join();
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
}
@Override // Binder call
public void clearUserActivityTimeout(long now, long timeout) {
// TODO Auto-generated method stub
// Only used by phone app, delete this
}
@Override // Binder call
public void setPokeLock(int pokey, IBinder lock, String tag) {
// TODO Auto-generated method stub
// Only used by phone app, delete this
}
/**
* Set the setting that determines whether the device stays on when plugged in.
* The argument is a bit string, with each bit specifying a power source that,
* when the device is connected to that source, causes the device to stay on.
* See {@link android.os.BatteryManager} for the list of power sources that
* can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
* and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
*
* Used by "adb shell svc power stayon ..."
*
* @param val an {@code int} containing the bits that specify which power sources
* should cause the device to stay on.
*/
@Override // Binder call
public void setStayOnSetting(int val) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
final long ident = Binder.clearCallingIdentity();
try {
setStayOnSettingInternal(val);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setStayOnSettingInternal(int val) {
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, val);
}
/**
* Used by device administration to set the maximum screen off timeout.
*
* This method must only be called by the device administration policy manager.
*/
@Override // Binder call
public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) {
final long ident = Binder.clearCallingIdentity();
try {
setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
synchronized (mLock) {
mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() {
return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0
&& mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE;
}
@Override // Binder call
public void preventScreenOn(boolean prevent) {
// TODO Auto-generated method stub
// Only used by phone app, delete this
}
/**
* Used by the phone application to make the attention LED flash when ringing.
*/
@Override // Binder call
public void setAttentionLight(boolean on, int color) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setAttentionLightInternal(on, color);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setAttentionLightInternal(boolean on, int color) {
LightsService.Light light;
synchronized (mLock) {
if (!mSystemReady) {
return;
}
light = mAttentionLight;
}
// Control light outside of lock.
light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
}
/**
* Used by the Watchdog.
*/
public long timeSinceScreenWasLastOn() {
synchronized (mLock) {
if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
return 0;
}
return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime;
}
}
/**
* Used by the window manager to override the screen brightness based on the
* current foreground activity.
*
* This method must only be called by the window manager.
*
* @param brightness The overridden brightness, or -1 to disable the override.
*/
public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
synchronized (mLock) {
if (mScreenBrightnessOverrideFromWindowManager != brightness) {
mScreenBrightnessOverrideFromWindowManager = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Used by the window manager to override the button brightness based on the
* current foreground activity.
*
* This method must only be called by the window manager.
*
* @param brightness The overridden brightness, or -1 to disable the override.
*/
public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
// Do nothing.
// Button lights are not currently supported in the new implementation.
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
}
/**
* Used by the settings application and brightness control widgets to
* temporarily override the current screen brightness setting so that the
* user can observe the effect of an intended settings change without applying
* it immediately.
*
* The override will be canceled when the setting value is next updated.
*
* @param brightness The overridden brightness.
*
* @see Settings.System#SCREEN_BRIGHTNESS
*/
@Override // Binder call
public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
synchronized (mLock) {
if (mTemporaryScreenBrightnessSettingOverride != brightness) {
mTemporaryScreenBrightnessSettingOverride = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Used by the settings application and brightness control widgets to
* temporarily override the current screen auto-brightness adjustment setting so that the
* user can observe the effect of an intended settings change without applying
* it immediately.
*
* The override will be canceled when the setting value is next updated.
*
* @param adj The overridden brightness, or Float.NaN to disable the override.
*
* @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
*/
@Override // Binder call
public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
synchronized (mLock) {
// Note: This condition handles NaN because NaN is not equal to any other
// value, including itself.
if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
nativeShutdown();
}
/**
* Low-level function to reboot the device.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @throws IOException if reboot fails for some reason (eg, lack of
* permission)
*/
public static void lowLevelReboot(String reason) throws IOException {
nativeReboot(reason);
}
@Override // Watchdog.Monitor implementation
public void monitor() {
// Grab and release lock for watchdog monitor to detect deadlocks.
synchronized (mLock) {
}
}
@Override // Binder call
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump PowerManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
pw.println("POWER MANAGER (dumpsys power)\n");
final DisplayPowerController dpc;
synchronized (mLock) {
pw.println("Power Manager State:");
pw.println(" mDirty=0x" + Integer.toHexString(mDirty));
pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness));
pw.println(" mIsPowered=" + mIsPowered);
pw.println(" mStayOn=" + mStayOn);
pw.println(" mBootCompleted=" + mBootCompleted);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
pw.println(" mSendWakeUpFinishedNotificationWhenReady="
+ mSendWakeUpFinishedNotificationWhenReady);
pw.println(" mSendGoToSleepFinishedNotificationWhenReady="
+ mSendGoToSleepFinishedNotificationWhenReady);
pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
pw.println(" mLastUserActivityTimeNoChangeLights="
+ TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
pw.println(" mDisplayReady=" + mDisplayReady);
pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
pw.println();
pw.println("Settings and Configuration:");
pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig);
pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting);
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin="
+ mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+ isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting);
pw.println(" mScreenAutoBrightnessAdjustmentSetting="
+ mScreenAutoBrightnessAdjustmentSetting);
pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
pw.println(" mScreenBrightnessOverrideFromWindowManager="
+ mScreenBrightnessOverrideFromWindowManager);
pw.println(" mTemporaryScreenBrightnessSettingOverride="
+ mTemporaryScreenBrightnessSettingOverride);
pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
}
pw.println();
pw.println("Suspend Blockers: size=" + mSuspendBlockers.size());
for (SuspendBlocker sb : mSuspendBlockers) {
pw.println(" " + sb);
}
dpc = mDisplayPowerController;
}
if (dpc != null) {
dpc.dump(pw);
}
}
private SuspendBlocker createSuspendBlockerLocked(String name) {
SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
mSuspendBlockers.add(suspendBlocker);
return suspendBlocker;
}
private static String wakefulnessToString(int wakefulness) {
switch (wakefulness) {
case WAKEFULNESS_ASLEEP:
return "Asleep";
case WAKEFULNESS_AWAKE:
return "Awake";
case WAKEFULNESS_DREAMING:
return "Dreaming";
case WAKEFULNESS_NAPPING:
return "Napping";
default:
return Integer.toString(wakefulness);
}
}
private static WorkSource copyWorkSource(WorkSource workSource) {
return workSource != null ? new WorkSource(workSource) : null;
}
private final class BatteryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
handleBatteryStateChangedLocked();
}
}
}
private final class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
handleBootCompletedLocked();
}
}
}
private final class DockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED);
handleDockStateChangedLocked(dockState);
}
}
}
private final class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
synchronized (mLock) {
handleSettingsChangedLocked();
}
}
}
private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
new WindowManagerPolicy.ScreenOnListener() {
@Override
public void onScreenOn() {
}
};
/**
* Handler for asynchronous operations performed by the power manager.
*/
private final class PowerManagerHandler extends Handler {
public PowerManagerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
break;
case MSG_SANDMAN:
handleSandman();
break;
}
}
}
/**
* Represents a wake lock that has been acquired by an application.
*/
private final class WakeLock implements IBinder.DeathRecipient {
public final IBinder mLock;
public int mFlags;
public String mTag;
public WorkSource mWorkSource;
public int mOwnerUid;
public int mOwnerPid;
public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
mLock = lock;
mFlags = flags;
mTag = tag;
mWorkSource = copyWorkSource(workSource);
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
@Override
public void binderDied() {
PowerManagerService.this.handleWakeLockDeath(this);
}
public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
return mFlags == flags
&& mTag.equals(tag)
&& hasSameWorkSource(workSource)
&& mOwnerUid == ownerUid
&& mOwnerPid == ownerPid;
}
public void updateProperties(int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
mFlags = flags;
mTag = tag;
updateWorkSource(workSource);
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
public boolean hasSameWorkSource(WorkSource workSource) {
return Objects.equal(mWorkSource, workSource);
}
public void updateWorkSource(WorkSource workSource) {
mWorkSource = copyWorkSource(workSource);
}
@Override
public String toString() {
return getLockLevelString()
+ " '" + mTag + "'" + getLockFlagsString()
+ " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
}
private String getLockLevelString() {
switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
return "FULL_WAKE_LOCK ";
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
return "SCREEN_BRIGHT_WAKE_LOCK ";
case PowerManager.SCREEN_DIM_WAKE_LOCK:
return "SCREEN_DIM_WAKE_LOCK ";
case PowerManager.PARTIAL_WAKE_LOCK:
return "PARTIAL_WAKE_LOCK ";
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
default:
return "??? ";
}
}
private String getLockFlagsString() {
String result = "";
if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
result += " ACQUIRE_CAUSES_WAKEUP";
}
if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
result += " ON_AFTER_RELEASE";
}
return result;
}
}
private final class SuspendBlockerImpl implements SuspendBlocker {
private final String mName;
private int mReferenceCount;
public SuspendBlockerImpl(String name) {
mName = name;
}
@Override
protected void finalize() throws Throwable {
try {
if (mReferenceCount != 0) {
Log.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
nativeReleaseSuspendBlocker(mName);
}
} finally {
super.finalize();
}
}
@Override
public void acquire() {
synchronized (this) {
mReferenceCount += 1;
if (mReferenceCount == 1) {
nativeAcquireSuspendBlocker(mName);
}
}
}
@Override
public void release() {
synchronized (this) {
mReferenceCount -= 1;
if (mReferenceCount == 0) {
nativeReleaseSuspendBlocker(mName);
} else if (mReferenceCount < 0) {
Log.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was released without being acquired!", new Throwable());
mReferenceCount = 0;
}
}
}
@Override
public String toString() {
synchronized (this) {
return mName + ": ref count=" + mReferenceCount;
}
}
}
}