Merge "Make DreamManagerService more robust." into jb-mr1-dev
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.");
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user