Merge "Make DreamManagerService more robust." into jb-mr1-dev

This commit is contained in:
Jeff Brown
2012-09-26 17:25:22 -07:00
committed by Android (Google) Code Review
13 changed files with 634 additions and 596 deletions

View File

@@ -34,6 +34,7 @@ interface IPowerManager
void userActivity(long time, int event, int flags);
void wakeUp(long time);
void goToSleep(long time, int reason);
void nap(long time);
boolean isScreenOn();
void reboot(String reason);

View File

@@ -426,7 +426,7 @@ public final class PowerManager {
* </p>
*
* @param when The time of the user activity, in the {@link SystemClock#uptimeMillis()}
* time base. This timestamp is used to correctly order the user activity with
* time base. This timestamp is used to correctly order the user activity request with
* other power management functions. It should be set
* to the timestamp of the input event that caused the user activity.
* @param noChangeLights If true, does not cause the keyboard backlight to turn on
@@ -457,7 +457,7 @@ public final class PowerManager {
*
* @param time The time when the request to go to sleep was issued, in the
* {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
* order the user activity with other power management functions. It should be set
* order the go to sleep request with other power management functions. It should be set
* to the timestamp of the input event that caused the request to go to sleep.
*
* @see #userActivity
@@ -481,7 +481,7 @@ public final class PowerManager {
*
* @param time The time when the request to wake up was issued, in the
* {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
* order the user activity with other power management functions. It should be set
* order the wake up request with other power management functions. It should be set
* to the timestamp of the input event that caused the request to wake up.
*
* @see #userActivity
@@ -494,6 +494,34 @@ public final class PowerManager {
}
}
/**
* Forces the device to start napping.
* <p>
* If the device is currently awake, starts dreaming, otherwise does nothing.
* When the dream ends or if the dream cannot be started, the device will
* either wake up or go to sleep depending on whether there has been recent
* user activity.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
* </p>
*
* @param time The time when the request to nap was issued, in the
* {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
* order the nap request with other power management functions. It should be set
* to the timestamp of the input event that caused the request to nap.
*
* @see #wakeUp
* @see #goToSleep
*
* @hide
*/
public void nap(long time) {
try {
mService.nap(time);
} catch (RemoteException e) {
}
}
/**
* Sets the brightness of the backlights (screen, keyboard, button).
* <p>

View File

@@ -71,6 +71,12 @@ public class Dream extends Service implements Window.Callback {
private final static boolean DEBUG = true;
private final String TAG = Dream.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
/**
* The name of the dream manager service.
* @hide
*/
public static final String DREAM_SERVICE = "dreams";
/**
* Used with {@link Intent#ACTION_MAIN} to declare the necessary intent-filter for a dream.
*
@@ -499,7 +505,7 @@ public class Dream extends Service implements Window.Callback {
// end public api
private void loadSandman() {
mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
}
private final void attach(IBinder windowToken) {
@@ -584,7 +590,7 @@ public class Dream extends Service implements Window.Callback {
mFinished = true;
if (mSandman != null) {
mSandman.awakenSelf(mWindowToken);
mSandman.finishSelf(mWindowToken);
} else {
Slog.w(TAG, "No dream manager found");
}

View File

@@ -30,5 +30,5 @@ interface IDreamManager {
ComponentName getDefaultDreamComponent();
void testDream(in ComponentName componentName);
boolean isDreaming();
void awakenSelf(in IBinder token);
void finishSelf(in IBinder token);
}

View File

@@ -20,6 +20,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.dreams.Dream;
import android.service.dreams.IDreamManager;
import android.util.Slog;
@@ -45,7 +46,7 @@ public class Somnambulator extends Activity {
setResult(RESULT_OK, resultIntent);
} else {
IDreamManager somnambulist = IDreamManager.Stub.asInterface(
ServiceManager.checkService("dreams"));
ServiceManager.checkService(Dream.DREAM_SERVICE));
if (somnambulist != null) {
try {
Slog.v("Somnambulator", "Dreaming by user request.");

View File

@@ -45,6 +45,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Dream;
import android.service.dreams.IDreamManager;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -262,7 +263,7 @@ public class PhoneStatusBar extends BaseStatusBar {
.getDefaultDisplay();
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService("dreams"));
ServiceManager.checkService(Dream.DREAM_SERVICE));
super.start(); // calls createAndAddWindows()

View File

@@ -16,9 +16,6 @@
package com.android.server;
import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -27,16 +24,12 @@ import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.util.Log;
import android.util.Slog;
@@ -48,14 +41,10 @@ import java.io.FileReader;
*/
final class DockObserver extends UEventObserver {
private static final String TAG = DockObserver.class.getSimpleName();
private static final boolean LOG = false;
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
private static final int MSG_DOCK_STATE_CHANGED = 0;
private final Object mLock = new Object();
@@ -66,11 +55,16 @@ final class DockObserver extends UEventObserver {
private boolean mSystemReady;
private final Context mContext;
private final PowerManager mPowerManager;
private final PowerManager.WakeLock mWakeLock;
public DockObserver(Context context) {
mContext = context;
init(); // set initial status
mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
init(); // set initial status
startObserving(DOCK_UEVENT_MATCH);
}
@@ -87,17 +81,9 @@ final class DockObserver extends UEventObserver {
mPreviousDockState = mDockState;
mDockState = newState;
if (mSystemReady) {
// Don't force screen on when undocking from the desk dock.
// The change in power state will do this anyway.
// FIXME - we should be configurable.
if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
PowerManager pm =
(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
pm.wakeUp(SystemClock.uptimeMillis());
}
// Wake up immediately when docked or undocked.
mPowerManager.wakeUp(SystemClock.uptimeMillis());
updateLocked();
}
}
@@ -138,6 +124,7 @@ final class DockObserver extends UEventObserver {
}
private void updateLocked() {
mWakeLock.acquire();
mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
}
@@ -145,8 +132,8 @@ final class DockObserver extends UEventObserver {
synchronized (mLock) {
Slog.i(TAG, "Dock state changed: " + mDockState);
// Skip the dock intent if not yet provisioned.
final ContentResolver cr = mContext.getContentResolver();
if (Settings.Global.getInt(cr,
Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
@@ -158,16 +145,8 @@ final class DockObserver extends UEventObserver {
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
// Check if this is Bluetooth Dock
// TODO(BT): Get Dock address.
// String address = null;
// if (address != null) {
// intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
// BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
// }
// User feedback to confirm dock connection. Particularly
// useful for flaky contact pins...
// Play a sound to provide feedback to confirm dock connection.
// Particularly useful for flaky contact pins...
if (Settings.Global.getInt(cr,
Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) {
String whichSound = null;
@@ -204,44 +183,16 @@ final class DockObserver extends UEventObserver {
}
}
IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
if (mgr != null) {
// dreams feature enabled
boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED;
if (undocked) {
try {
if (mgr.isDreaming()) {
mgr.awaken();
}
} catch (RemoteException e) {
Slog.w(TAG, "Unable to awaken!", e);
}
} else {
if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) {
try {
mgr.dream();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to dream!", e);
}
}
}
} else {
// dreams feature not enabled, send legacy intent
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
// Send the dock event intent.
// There are many components in the system watching for this so as to
// adjust audio routing, screen orientation, etc.
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
// Release the wake lock that was acquired when the message was posted.
mWakeLock.release();
}
}
private static boolean isScreenSaverEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0;
}
private static boolean isScreenSaverActivatedOnDock(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
}
private final Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {

View File

@@ -38,6 +38,7 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.server.search.SearchManagerService;
import android.service.dreams.Dream;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -739,8 +740,8 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "Dreams Service");
// Dreams (interactive idle-time views, a/k/a screen savers)
dreamy = new DreamManagerService(context);
ServiceManager.addService("dreams", dreamy);
dreamy = new DreamManagerService(context, wmHandler);
ServiceManager.addService(Dream.DREAM_SERVICE, dreamy);
} catch (Throwable e) {
reportWtf("starting DreamManagerService", e);
}
@@ -811,7 +812,7 @@ class ServerThread extends Thread {
context.getResources().updateConfiguration(config, metrics);
try {
power.systemReady(twilight);
power.systemReady(twilight, dreamy);
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}

View File

@@ -17,6 +17,7 @@
package com.android.server;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IUiModeManager;
import android.app.Notification;
@@ -39,6 +40,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Dream;
import android.service.dreams.IDreamManager;
import android.util.Slog;
import java.io.FileDescriptor;
@@ -56,6 +59,9 @@ class UiModeManagerService extends IUiModeManager.Stub {
private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
private final Context mContext;
private final TwilightService mTwilightService;
private final Handler mHandler = new Handler();
@@ -110,72 +116,10 @@ class UiModeManagerService extends IUiModeManager.Stub {
return;
}
final int enableFlags = intent.getIntExtra("enableFlags", 0);
final int disableFlags = intent.getIntExtra("disableFlags", 0);
final int enableFlags = intent.getIntExtra("enableFlags", 0);
final int disableFlags = intent.getIntExtra("disableFlags", 0);
synchronized (mLock) {
// Launch a dock activity
String category = null;
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
// Only launch car home when car mode is enabled and the caller
// has asked us to switch to it.
if (ENABLE_LAUNCH_CAR_DOCK_APP
&& (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_CAR_DOCK;
}
} else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
// Only launch car home when desk mode is enabled and the caller
// has asked us to switch to it. Currently re-using the car
// mode flag since we don't have a formal API for "desk mode".
if (ENABLE_LAUNCH_DESK_DOCK_APP
&& (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_DESK_DOCK;
}
} else {
// Launch the standard home app if requested.
if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
category = Intent.CATEGORY_HOME;
}
}
if (LOG) {
Slog.v(TAG, String.format(
"Handling broadcast result for action %s: enable=0x%08x disable=0x%08x category=%s",
intent.getAction(), enableFlags, disableFlags, category));
}
if (category != null) {
// This is the new activity that will serve as home while
// we are in care mode.
Intent homeIntent = buildHomeIntent(category);
// Now we are going to be careful about switching the
// configuration and starting the activity -- we need to
// do this in a specific order under control of the
// activity manager, to do it cleanly. So compute the
// new config, but don't set it yet, and let the
// activity manager take care of both the start and config
// change.
Configuration newConfig = null;
if (mHoldingConfiguration) {
mHoldingConfiguration = false;
updateConfigurationLocked(false);
newConfig = mConfiguration;
}
try {
ActivityManagerNative.getDefault().startActivityWithConfig(
null, homeIntent, null, null, null, 0, 0,
newConfig, null, UserHandle.USER_CURRENT);
mHoldingConfiguration = false;
} catch (RemoteException e) {
Slog.w(TAG, e.getCause());
}
}
if (mHoldingConfiguration) {
mHoldingConfiguration = false;
updateConfigurationLocked(true);
}
updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags);
}
}
};
@@ -335,9 +279,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
}
final void updateConfigurationLocked(boolean sendIt) {
int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION
: mDefaultUiModeType;
final void updateConfigurationLocked() {
int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION : mDefaultUiModeType;
if (mCarModeEnabled) {
uiMode = Configuration.UI_MODE_TYPE_CAR;
} else if (isDeskDockState(mDockState)) {
@@ -365,17 +308,19 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
mCurUiMode = uiMode;
if (!mHoldingConfiguration && uiMode != mSetUiMode) {
mSetUiMode = uiMode;
if (!mHoldingConfiguration) {
mConfiguration.uiMode = uiMode;
}
}
if (sendIt) {
try {
ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);
} catch (RemoteException e) {
Slog.w(TAG, "Failure communicating with activity manager", e);
}
final void sendConfigurationLocked() {
if (mSetUiMode != mConfiguration.uiMode) {
mSetUiMode = mConfiguration.uiMode;
try {
ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);
} catch (RemoteException e) {
Slog.w(TAG, "Failure communicating with activity manager", e);
}
}
}
@@ -434,43 +379,38 @@ class UiModeManagerService extends IUiModeManager.Stub {
intent.putExtra("disableFlags", disableFlags);
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mResultReceiver, null, Activity.RESULT_OK, null, null);
// Attempting to make this transition a little more clean, we are going
// to hold off on doing a configuration change until we have finished
// the broadcast and started the home activity.
mHoldingConfiguration = true;
updateConfigurationLocked();
} else {
Intent homeIntent = null;
String category = null;
if (mCarModeEnabled) {
if (ENABLE_LAUNCH_CAR_DOCK_APP
&& (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK);
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_CAR_DOCK;
}
} else if (isDeskDockState(mDockState)) {
if (ENABLE_LAUNCH_DESK_DOCK_APP
&& (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK);
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_DESK_DOCK;
}
} else {
if ((disableFlags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
homeIntent = buildHomeIntent(Intent.CATEGORY_HOME);
if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
category = Intent.CATEGORY_HOME;
}
}
if (LOG) {
Slog.v(TAG, "updateLocked: null action, mDockState="
+ mDockState +", firing homeIntent: " + homeIntent);
+ mDockState +", category=" + category);
}
if (homeIntent != null) {
try {
mContext.startActivityAsUser(homeIntent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
}
}
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
updateConfigurationLocked(true);
// keep screen on when charging and in car mode
boolean keepScreenOn = mCharging &&
((mCarModeEnabled && mCarModeKeepsScreenOn) ||
@@ -487,6 +427,100 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
}
private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
// Launch a dock activity
String category = null;
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
// Only launch car home when car mode is enabled and the caller
// has asked us to switch to it.
if (ENABLE_LAUNCH_CAR_DOCK_APP
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_CAR_DOCK;
}
} else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) {
// Only launch car home when desk mode is enabled and the caller
// has asked us to switch to it. Currently re-using the car
// mode flag since we don't have a formal API for "desk mode".
if (ENABLE_LAUNCH_DESK_DOCK_APP
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_DESK_DOCK;
}
} else {
// Launch the standard home app if requested.
if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
category = Intent.CATEGORY_HOME;
}
}
if (LOG) {
Slog.v(TAG, String.format(
"Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, "
+ "category=%s",
action, enableFlags, disableFlags, category));
}
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
// Update the configuration but don't send it yet.
mHoldingConfiguration = false;
updateConfigurationLocked();
// Start the dock app, if there is one.
boolean dockAppStarted = false;
if (category != null) {
// Now we are going to be careful about switching the
// configuration and starting the activity -- we need to
// do this in a specific order under control of the
// activity manager, to do it cleanly. So compute the
// new config, but don't set it yet, and let the
// activity manager take care of both the start and config
// change.
Intent homeIntent = buildHomeIntent(category);
try {
int result = ActivityManagerNative.getDefault().startActivityWithConfig(
null, homeIntent, null, null, null, 0, 0,
mConfiguration, null, UserHandle.USER_CURRENT);
if (result >= ActivityManager.START_SUCCESS) {
dockAppStarted = true;
} else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
Slog.e(TAG, "Could not start dock app: " + homeIntent
+ ", startActivityWithConfig result " + result);
}
} catch (RemoteException ex) {
Slog.e(TAG, "Could not start dock app: " + homeIntent, ex);
}
}
// Send the new configuration.
sendConfigurationLocked();
// If we did not start a dock app, then start dreaming if supported.
if (!dockAppStarted && isScreenSaverEnabled() && isScreenSaverActivatedOnDock()) {
Slog.i(TAG, "Activating dream while docked.");
try {
IDreamManager dreamManagerService = IDreamManager.Stub.asInterface(
ServiceManager.getService(Dream.DREAM_SERVICE));
dreamManagerService.dream();
} catch (RemoteException ex) {
Slog.e(TAG, "Could not start dream when docked.", ex);
}
}
}
private boolean isScreenSaverEnabled() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED,
UserHandle.USER_CURRENT) != 0;
}
private boolean isScreenSaverActivatedOnDock() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0;
}
private void adjustStatusBarCarModeLocked() {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);

View File

@@ -25,193 +25,219 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.IBinder.DeathRecipient;
import android.service.dreams.Dream;
import android.service.dreams.IDreamService;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.util.DumpUtils;
import java.io.PrintWriter;
import java.util.NoSuchElementException;
/**
* Internal controller for starting and stopping the current dream and managing related state.
*
* Assumes all operations (except {@link #dump}) are called from a single thread.
* Assumes all operations are called from the dream handler thread.
*/
final class DreamController {
private static final boolean DEBUG = true;
private static final String TAG = DreamController.class.getSimpleName();
public interface Listener {
void onDreamStopped(boolean wasTest);
}
private static final String TAG = "DreamController";
private final Context mContext;
private final IWindowManager mIWindowManager;
private final DeathRecipient mDeathRecipient;
private final ServiceConnection mServiceConnection;
private final Handler mHandler;
private final Listener mListener;
private final IWindowManager mIWindowManager;
private Handler mHandler;
private final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
private final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
private ComponentName mCurrentDreamComponent;
private IDreamService mCurrentDream;
private Binder mCurrentDreamToken;
private boolean mCurrentDreamIsTest;
private DreamRecord mCurrentDream;
public DreamController(Context context, DeathRecipient deathRecipient,
ServiceConnection serviceConnection, Listener listener) {
public DreamController(Context context, Handler handler, Listener listener) {
mContext = context;
mDeathRecipient = deathRecipient;
mServiceConnection = serviceConnection;
mHandler = handler;
mListener = listener;
mIWindowManager = WindowManagerGlobal.getWindowManagerService();
}
public void setHandler(Handler handler) {
mHandler = handler;
public void dump(PrintWriter pw) {
pw.println("Dreamland:");
if (mCurrentDream != null) {
pw.println(" mCurrentDream:");
pw.println(" mToken=" + mCurrentDream.mToken);
pw.println(" mName=" + mCurrentDream.mName);
pw.println(" mIsTest=" + mCurrentDream.mIsTest);
pw.println(" mUserId=" + mCurrentDream.mUserId);
pw.println(" mBound=" + mCurrentDream.mBound);
pw.println(" mService=" + mCurrentDream.mService);
pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast);
} else {
pw.println(" mCurrentDream: null");
}
}
public void dump(PrintWriter pw) {
if (mHandler== null || pw == null) {
public void startDream(Binder token, ComponentName name, boolean isTest, int userId) {
stopDream();
Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId);
mCurrentDream = new DreamRecord(token, name, isTest, userId);
try {
mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
} catch (RemoteException ex) {
Slog.e(TAG, "Unable to add window token for dream.", ex);
stopDream();
return;
}
DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
@Override
public void dump(PrintWriter pw) {
pw.print(" component="); pw.println(mCurrentDreamComponent);
pw.print(" token="); pw.println(mCurrentDreamToken);
pw.print(" dream="); pw.println(mCurrentDream);
}
}, pw, 200);
}
public void start(ComponentName dream, boolean isTest) {
if (DEBUG) Slog.v(TAG, String.format("start(%s,%s)", dream, isTest));
if (mCurrentDreamComponent != null ) {
if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) {
if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Dream.CATEGORY_DREAM);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
try {
if (!mContext.bindService(intent, mCurrentDream,
Context.BIND_AUTO_CREATE, userId)) {
Slog.e(TAG, "Unable to bind dream service: " + intent);
stopDream();
return;
}
// stop the current dream before starting a new one
stop();
}
mCurrentDreamComponent = dream;
mCurrentDreamIsTest = isTest;
mCurrentDreamToken = new Binder();
try {
if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken
+ " for window type: " + WindowManager.LayoutParams.TYPE_DREAM);
mIWindowManager.addWindowToken(mCurrentDreamToken,
WindowManager.LayoutParams.TYPE_DREAM);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to add window token.");
stop();
} catch (SecurityException ex) {
Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
stopDream();
return;
}
Intent intent = new Intent(Intent.ACTION_MAIN)
.setComponent(mCurrentDreamComponent)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
.putExtra("android.dreams.TEST", mCurrentDreamIsTest);
if (!mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
Slog.w(TAG, "Unable to bind service");
stop();
return;
}
if (DEBUG) Slog.v(TAG, "Bound service");
mCurrentDream.mBound = true;
}
public void attach(ComponentName name, IBinder dream) {
if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream));
mCurrentDream = IDreamService.Stub.asInterface(dream);
boolean linked = linkDeathRecipient(dream);
if (!linked) {
stop();
public void stopDream() {
if (mCurrentDream == null) {
return;
}
try {
if (DEBUG) Slog.v(TAG, "Attaching with token:" + mCurrentDreamToken);
mCurrentDream.attach(mCurrentDreamToken);
} catch (Throwable ex) {
Slog.w(TAG, "Unable to send window token to dream:" + ex);
stop();
}
}
public void stop() {
if (DEBUG) Slog.v(TAG, "stop()");
if (mCurrentDream != null) {
unlinkDeathRecipient(mCurrentDream.asBinder());
if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream);
mContext.unbindService(mServiceConnection);
}
if (mCurrentDreamToken != null) {
removeWindowToken(mCurrentDreamToken);
}
final boolean wasTest = mCurrentDreamIsTest;
final DreamRecord oldDream = mCurrentDream;
mCurrentDream = null;
mCurrentDreamToken = null;
mCurrentDreamComponent = null;
mCurrentDreamIsTest = false;
Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
+ ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId);
if (mListener != null && mHandler != null) {
mHandler.post(new Runnable(){
if (oldDream.mSentStartBroadcast) {
mContext.sendBroadcast(mDreamingStoppedIntent);
}
if (oldDream.mService != null) {
// TODO: It would be nice to tell the dream that it's being stopped so that
// it can shut down nicely before we yank its window token out from under it.
try {
oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
} catch (NoSuchElementException ex) {
// don't care
}
oldDream.mService = null;
}
if (oldDream.mBound) {
mContext.unbindService(oldDream);
}
try {
mIWindowManager.removeWindowToken(oldDream.mToken);
} catch (RemoteException ex) {
Slog.w(TAG, "Error removing window token for dream.", ex);
}
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onDreamStopped(oldDream.mToken);
}
});
}
private void attach(IDreamService service) {
try {
service.asBinder().linkToDeath(mCurrentDream, 0);
service.attach(mCurrentDream.mToken);
} catch (RemoteException ex) {
Slog.e(TAG, "The dream service died unexpectedly.", ex);
stopDream();
return;
}
mCurrentDream.mService = service;
if (!mCurrentDream.mIsTest) {
mContext.sendBroadcast(mDreamingStartedIntent);
mCurrentDream.mSentStartBroadcast = true;
}
}
/**
* Callback interface to be implemented by the {@link DreamManagerService}.
*/
public interface Listener {
void onDreamStopped(Binder token);
}
private final class DreamRecord implements DeathRecipient, ServiceConnection {
public final Binder mToken;
public final ComponentName mName;
public final boolean mIsTest;
public final int mUserId;
public boolean mBound;
public IDreamService mService;
public boolean mSentStartBroadcast;
public DreamRecord(Binder token, ComponentName name,
boolean isTest, int userId) {
mToken = token;
mName = name;
mIsTest = isTest;
mUserId = userId;
}
// May be called on any thread.
@Override
public void binderDied() {
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onDreamStopped(wasTest);
}});
mService = null;
if (mCurrentDream == DreamRecord.this) {
stopDream();
}
}
});
}
// May be called on any thread.
@Override
public void onServiceConnected(ComponentName name, final IBinder service) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mCurrentDream == DreamRecord.this && mService == null) {
attach(IDreamService.Stub.asInterface(service));
}
}
});
}
// May be called on any thread.
@Override
public void onServiceDisconnected(ComponentName name) {
mHandler.post(new Runnable() {
@Override
public void run() {
mService = null;
if (mCurrentDream == DreamRecord.this) {
stopDream();
}
}
});
}
}
public void stopSelf(IBinder token) {
if (DEBUG) Slog.v(TAG, String.format("stopSelf(%s)", token));
if (token == null || token != mCurrentDreamToken) {
Slog.w(TAG, "Stop requested for non-current dream token: " + token);
} else {
stop();
}
}
private void removeWindowToken(IBinder token) {
if (DEBUG) Slog.v(TAG, "Removing window token: " + token);
try {
mIWindowManager.removeWindowToken(token);
} catch (Throwable e) {
Slog.w(TAG, "Error removing window token", e);
}
}
private boolean linkDeathRecipient(IBinder dream) {
if (DEBUG) Slog.v(TAG, "Linking death recipient");
try {
dream.linkToDeath(mDeathRecipient, 0);
return true;
} catch (RemoteException e) {
Slog.w(TAG, "Unable to link death recipient", e);
return false;
}
}
private void unlinkDeathRecipient(IBinder dream) {
if (DEBUG) Slog.v(TAG, "Unlinking death recipient");
try {
dream.unlinkToDeath(mDeathRecipient, 0);
} catch (NoSuchElementException e) {
// we tried
}
}
}

View File

@@ -16,100 +16,97 @@
package com.android.server.dreams;
import static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS;
import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT;
import com.android.internal.util.DumpUtils;
import android.app.ActivityManagerNative;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Dream;
import android.service.dreams.IDreamManager;
import android.util.Slog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import libcore.util.Objects;
/**
* Service api for managing dreams.
*
* @hide
*/
public final class DreamManagerService
extends IDreamManager.Stub
implements ServiceConnection {
public final class DreamManagerService extends IDreamManager.Stub {
private static final boolean DEBUG = true;
private static final String TAG = DreamManagerService.class.getSimpleName();
private static final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
private static final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
private static final String TAG = "DreamManagerService";
private final Object mLock = new Object();
private final DreamController mController;
private final DreamControllerHandler mHandler;
private final Context mContext;
private final DreamHandler mHandler;
private final DreamController mController;
private final PowerManager mPowerManager;
private final CurrentUserManager mCurrentUserManager = new CurrentUserManager();
private Binder mCurrentDreamToken;
private ComponentName mCurrentDreamName;
private int mCurrentDreamUserId;
private boolean mCurrentDreamIsTest;
private final DeathRecipient mAwakenOnBinderDeath = new DeathRecipient() {
@Override
public void binderDied() {
if (DEBUG) Slog.v(TAG, "binderDied()");
awaken();
}
};
private final DreamController.Listener mControllerListener = new DreamController.Listener() {
@Override
public void onDreamStopped(boolean wasTest) {
synchronized(mLock) {
setDreamingLocked(false, wasTest);
}
}};
private boolean mIsDreaming;
public DreamManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "DreamManagerService startup");
public DreamManagerService(Context context, Handler mainHandler) {
mContext = context;
mController = new DreamController(context, mAwakenOnBinderDeath, this, mControllerListener);
mHandler = new DreamControllerHandler(mController);
mController.setHandler(mHandler);
mHandler = new DreamHandler(mainHandler.getLooper());
mController = new DreamController(context, mHandler, mControllerListener);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
}
public void systemReady() {
mCurrentUserManager.init(mContext);
if (DEBUG) Slog.v(TAG, "Ready to dream!");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
stopDreamLocked();
}
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
pw.println("Dreamland:");
mController.dump(pw);
mCurrentUserManager.dump(pw);
pw.println("DREAM MANAGER (dumpsys dreams)");
pw.println();
pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
pw.println("mCurrentDreamName=" + mCurrentDreamName);
pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
pw.println();
DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
@Override
public void dump(PrintWriter pw) {
mController.dump(pw);
}
}, pw, 200);
}
// begin IDreamManager api
@Override
@Override // Binder call
public ComponentName[] getDreamComponents() {
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
int userId = UserHandle.getCallingUserId();
final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
return getDreamComponentsForUser(userId);
@@ -118,15 +115,15 @@ public final class DreamManagerService
}
}
@Override
@Override // Binder call
public void setDreamComponents(ComponentName[] componentNames) {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
int userId = UserHandle.getCallingUserId();
final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
SCREENSAVER_COMPONENTS,
Settings.Secure.SCREENSAVER_COMPONENTS,
componentsToString(componentNames),
userId);
} finally {
@@ -134,142 +131,213 @@ public final class DreamManagerService
}
}
@Override
@Override // Binder call
public ComponentName getDefaultDreamComponent() {
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
int userId = UserHandle.getCallingUserId();
final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
SCREENSAVER_DEFAULT_COMPONENT,
Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
userId);
return name == null ? null : ComponentName.unflattenFromString(name);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
@Override // Binder call
public boolean isDreaming() {
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
return mIsDreaming;
synchronized (mLock) {
return mCurrentDreamToken != null && !mCurrentDreamIsTest;
}
}
@Override
@Override // Binder call
public void dream() {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Dream now");
ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserManager.getCurrentUserId());
ComponentName firstDream = dreams != null && dreams.length > 0 ? dreams[0] : null;
if (firstDream != null) {
mHandler.requestStart(firstDream, false /*isTest*/);
synchronized (mLock) {
setDreamingLocked(true, false /*isTest*/);
}
}
// Ask the power manager to nap. It will eventually call back into
// startDream() if/when it is appropriate to start dreaming.
// Because napping could cause the screen to turn off immediately if the dream
// cannot be started, we keep one eye open and gently poke user activity.
long time = SystemClock.uptimeMillis();
mPowerManager.userActivity(time, true /*noChangeLights*/);
mPowerManager.nap(time);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
@Override // Binder call
public void testDream(ComponentName dream) {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
if (dream == null) {
throw new IllegalArgumentException("dream must not be null");
}
final int callingUserId = UserHandle.getCallingUserId();
final int currentUserId = ActivityManager.getCurrentUser();
if (callingUserId != currentUserId) {
// This check is inherently prone to races but at least it's something.
Slog.w(TAG, "Aborted attempt to start a test dream while a different "
+ " user is active: callingUserId=" + callingUserId
+ ", currentUserId=" + currentUserId);
return;
}
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Test dream name=" + dream);
if (dream != null) {
mHandler.requestStart(dream, true /*isTest*/);
synchronized (mLock) {
setDreamingLocked(true, true /*isTest*/);
}
synchronized (mLock) {
startDreamLocked(dream, true /*isTest*/, callingUserId);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
@Override // Binder call
public void awaken() {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Wake up");
mHandler.requestStop();
// Treat an explicit request to awaken as user activity so that the
// device doesn't immediately go to sleep if the timeout expired,
// for example when being undocked.
long time = SystemClock.uptimeMillis();
mPowerManager.userActivity(time, false /*noChangeLights*/);
stopDream();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void awakenSelf(IBinder token) {
// requires no permission, called by Dream from an arbitrary process
@Override // Binder call
public void finishSelf(IBinder token) {
// Requires no permission, called by Dream from an arbitrary process.
if (token == null) {
throw new IllegalArgumentException("token must not be null");
}
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Wake up from dream: " + token);
if (token != null) {
mHandler.requestStopSelf(token);
if (DEBUG) {
Slog.d(TAG, "Dream finished: " + token);
}
// Note that a dream finishing and self-terminating is not
// itself considered user activity. If the dream is ending because
// the user interacted with the device then user activity will already
// have been poked so the device will stay awake a bit longer.
// If the dream is ending on its own for other reasons and no wake
// locks are held and the user activity timeout has expired then the
// device may simply go to sleep.
synchronized (mLock) {
if (mCurrentDreamToken == token) {
stopDreamLocked();
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// end IDreamManager api
// begin ServiceConnection
@Override
public void onServiceConnected(ComponentName name, IBinder dream) {
if (DEBUG) Slog.v(TAG, "Service connected: " + name + " binder=" +
dream + " thread=" + Thread.currentThread().getId());
mHandler.requestAttach(name, dream);
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Slog.v(TAG, "Service disconnected: " + name);
// Only happens in exceptional circumstances, awaken just to be safe
awaken();
}
// end ServiceConnection
private void checkPermission(String permission) {
if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) {
throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ ", must have permission " + permission);
}
}
private void setDreamingLocked(boolean isDreaming, boolean isTest) {
boolean wasDreaming = mIsDreaming;
if (!isTest) {
if (!wasDreaming && isDreaming) {
if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STARTED");
mContext.sendBroadcast(mDreamingStartedIntent);
} else if (wasDreaming && !isDreaming) {
if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STOPPED");
mContext.sendBroadcast(mDreamingStoppedIntent);
/**
* Called by the power manager to start a dream.
*/
public void startDream() {
int userId = ActivityManager.getCurrentUser();
ComponentName dream = chooseDreamForUser(userId);
if (dream != null) {
synchronized (mLock) {
startDreamLocked(dream, false /*isTest*/, userId);
}
}
mIsDreaming = isDreaming;
}
/**
* Called by the power manager to stop a dream.
*/
public void stopDream() {
synchronized (mLock) {
stopDreamLocked();
}
}
private ComponentName chooseDreamForUser(int userId) {
ComponentName[] dreams = getDreamComponentsForUser(userId);
return dreams != null && dreams.length != 0 ? dreams[0] : null;
}
private ComponentName[] getDreamComponentsForUser(int userId) {
String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
SCREENSAVER_COMPONENTS,
Settings.Secure.SCREENSAVER_COMPONENTS,
userId);
return names == null ? null : componentsFromString(names);
}
private void startDreamLocked(final ComponentName name,
final boolean isTest, final int userId) {
if (Objects.equal(mCurrentDreamName, name)
&& mCurrentDreamIsTest == isTest
&& mCurrentDreamUserId == userId) {
return;
}
stopDreamLocked();
Slog.i(TAG, "Entering dreamland.");
final Binder newToken = new Binder();
mCurrentDreamToken = newToken;
mCurrentDreamName = name;
mCurrentDreamIsTest = isTest;
mCurrentDreamUserId = userId;
mHandler.post(new Runnable() {
@Override
public void run() {
mController.startDream(newToken, name, isTest, userId);
}
});
}
private void stopDreamLocked() {
if (mCurrentDreamToken != null) {
Slog.i(TAG, "Leaving dreamland.");
cleanupDreamLocked();
mHandler.post(new Runnable() {
@Override
public void run() {
mController.stopDream();
}
});
}
}
private void cleanupDreamLocked() {
mCurrentDreamToken = null;
mCurrentDreamName = null;
mCurrentDreamIsTest = false;
mCurrentDreamUserId = 0;
}
private void checkPermission(String permission) {
if (mContext.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ ", must have permission " + permission);
}
}
private static String componentsToString(ComponentName[] componentNames) {
StringBuilder names = new StringBuilder();
if (componentNames != null) {
@@ -292,93 +360,24 @@ public final class DreamManagerService
return componentNames;
}
/**
* Keeps track of the current user, since dream() uses the current user's configuration.
*/
private static class CurrentUserManager {
private final Object mLock = new Object();
private int mCurrentUserId;
public void init(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
synchronized(mLock) {
mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
}
}
}}, filter);
try {
synchronized (mLock) {
mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
private final DreamController.Listener mControllerListener = new DreamController.Listener() {
@Override
public void onDreamStopped(Binder token) {
synchronized (mLock) {
if (mCurrentDreamToken == token) {
cleanupDreamLocked();
}
} catch (RemoteException e) {
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
}
}
public void dump(PrintWriter pw) {
pw.print(" user="); pw.println(getCurrentUserId());
}
public int getCurrentUserId() {
synchronized(mLock) {
return mCurrentUserId;
}
}
}
};
/**
* Handler for asynchronous operations performed by the dream manager.
*
* Ensures operations to {@link DreamController} are single-threaded.
*/
private static final class DreamControllerHandler extends Handler {
private final DreamController mController;
private final Runnable mStopRunnable = new Runnable() {
@Override
public void run() {
mController.stop();
}};
public DreamControllerHandler(DreamController controller) {
super(true /*async*/);
mController = controller;
private final class DreamHandler extends Handler {
public DreamHandler(Looper looper) {
super(looper, null, true /*async*/);
}
public void requestStart(final ComponentName name, final boolean isTest) {
post(new Runnable(){
@Override
public void run() {
mController.start(name, isTest);
}});
}
public void requestAttach(final ComponentName name, final IBinder dream) {
post(new Runnable(){
@Override
public void run() {
mController.attach(name, dream);
}});
}
public void requestStopSelf(final IBinder token) {
post(new Runnable(){
@Override
public void run() {
mController.stopSelf(token);
}});
}
public void requestStop() {
post(mStopRunnable);
}
}
}

View File

@@ -24,6 +24,7 @@ import com.android.server.TwilightService;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -46,13 +47,11 @@ 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.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.Dream;
import android.service.dreams.IDreamManager;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -100,14 +99,12 @@ public final class PowerManagerService extends IPowerManager.Stub
private static final int DIRTY_STAY_ON = 1 << 7;
// Dirty bit: battery state changed
private static final int DIRTY_BATTERY_STATE = 1 << 8;
// Dirty bit: dream ended
private static final int DIRTY_DREAM_ENDED = 1 << 9;
// 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.
// When the user activity timeout expires, the device may start napping or go to sleep.
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
@@ -149,7 +146,7 @@ public final class PowerManagerService extends IPowerManager.Stub
private Notifier mNotifier;
private DisplayPowerController mDisplayPowerController;
private SettingsObserver mSettingsObserver;
private IDreamManager mDreamManager;
private DreamManagerService mDreamManager;
private LightsService.Light mAttentionLight;
private final Object mLock = new Object();
@@ -335,9 +332,10 @@ public final class PowerManagerService extends IPowerManager.Stub
}
}
public void systemReady(TwilightService twilight) {
public void systemReady(TwilightService twilight, DreamManagerService dreamManager) {
synchronized (mLock) {
mSystemReady = true;
mDreamManager = dreamManager;
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -365,10 +363,7 @@ public final class PowerManagerService extends IPowerManager.Stub
mContext.registerReceiver(new BootCompletedReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Dream.ACTION_DREAMING_STARTED);
filter.addAction(Dream.ACTION_DREAMING_STOPPED);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
@@ -887,6 +882,47 @@ public final class PowerManagerService extends IPowerManager.Stub
return true;
}
@Override // Binder call
public void nap(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 {
napInternal(eventTime);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void napInternal(long eventTime) {
synchronized (mLock) {
if (napNoUpdateLocked(eventTime)) {
updatePowerStateLocked();
}
}
}
private boolean napNoUpdateLocked(long eventTime) {
if (DEBUG_SPEW) {
Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime);
}
if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}
Slog.i(TAG, "Nap time...");
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_NAPPING;
return true;
}
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
@@ -1143,11 +1179,15 @@ public final class PowerManagerService extends IPowerManager.Stub
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) {
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Nap time...");
Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
}
final long time = SystemClock.uptimeMillis();
if (mDreamsActivateOnSleepSetting) {
changed = napNoUpdateLocked(time);
} else {
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
}
mWakefulness = WAKEFULNESS_NAPPING;
mDirty |= DIRTY_WAKEFULNESS;
changed = true;
}
}
return changed;
@@ -1172,8 +1212,7 @@ public final class PowerManagerService extends IPowerManager.Stub
| DIRTY_SETTINGS
| DIRTY_IS_POWERED
| DIRTY_STAY_ON
| DIRTY_BATTERY_STATE
| DIRTY_DREAM_ENDED)) != 0) {
| DIRTY_BATTERY_STATE)) != 0) {
scheduleSandmanLocked();
}
}
@@ -1210,32 +1249,15 @@ public final class PowerManagerService extends IPowerManager.Stub
}
}
// 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) {
if (startDreaming) {
mDreamManager.startDream();
}
isDreaming = mDreamManager.isDreaming();
}
// Update dream state.
@@ -1255,18 +1277,6 @@ public final class PowerManagerService extends IPowerManager.Stub
if (!continueDreaming) {
handleDreamFinishedLocked();
}
// In addition to listening for the intent, poll the sandman periodically to detect
// when the dream has ended (as a watchdog only, ensuring our state is always correct).
if (mWakefulness == WAKEFULNESS_DREAMING
|| mWakefulness == WAKEFULNESS_NAPPING) {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, 5000);
}
}
}
// Stop dreaming if needed.
@@ -1274,26 +1284,22 @@ public final class PowerManagerService extends IPowerManager.Stub
// If so, then the power manager will have posted another message to the handler
// to take care of it later.
if (mDreamManager != null) {
try {
if (!continueDreaming && isDreaming) {
Slog.i(TAG, "Leaving dreamland.");
mDreamManager.awaken();
}
} catch (RemoteException ex) {
if (!continueDreaming) {
mDreamManager.stopDream();
}
}
}
/**
* 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.
* assuming that there was either an explicit request to nap or the user activity
* timeout expired and no wake locks are held.
*/
private boolean canDreamLocked() {
return mIsPowered
&& mDreamsSupportedConfig
&& mDreamsEnabledSetting
&& mDreamsActivateOnSleepSetting
&& !mBatteryService.isBatteryLow();
&& mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
}
/**
@@ -1313,7 +1319,6 @@ public final class PowerManagerService extends IPowerManager.Stub
}
}
/**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true. The display
@@ -1494,15 +1499,6 @@ public final class PowerManagerService extends IPowerManager.Stub
updatePowerStateLocked();
}
private void handleDockStateChangedLocked(int dockState) {
// TODO
}
private void handleDreamEndedLocked() {
mDirty |= DIRTY_DREAM_ENDED;
updatePowerStateLocked();
}
/**
* Reboot the device immediately, passing 'reason' (may be null)
* to the underlying __reboot system call. Should not return.
@@ -1957,22 +1953,11 @@ public final class PowerManagerService extends IPowerManager.Stub
}
}
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 DreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
handleDreamEndedLocked();
scheduleSandmanLocked();
}
}
}

View File

@@ -59,6 +59,11 @@ public class BridgePowerManager implements IPowerManager {
// pass for now.
}
@Override
public void nap(long arg0) throws RemoteException {
// pass for now.
}
@Override
public void preventScreenOn(boolean arg0) throws RemoteException {
// pass for now.