summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2012-09-26 17:25:22 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-09-26 17:25:23 -0700
commit0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb (patch)
tree128f62103b9db114d27037404d9cef29d44b3d4f
parent5a864aa0d8919d38b23c9e7e90455181d3a979e3 (diff)
parent62c82e4d92cc0b856059f905d81885f7808a0e7d (diff)
downloadframeworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.zip
frameworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.tar.gz
frameworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.tar.bz2
Merge "Make DreamManagerService more robust." into jb-mr1-dev
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/os/PowerManager.java34
-rw-r--r--core/java/android/service/dreams/Dream.java10
-rw-r--r--core/java/android/service/dreams/IDreamManager.aidl2
-rw-r--r--packages/SystemUI/src/com/android/systemui/Somnambulator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java3
-rw-r--r--services/java/com/android/server/DockObserver.java89
-rw-r--r--services/java/com/android/server/SystemServer.java7
-rw-r--r--services/java/com/android/server/UiModeManagerService.java220
-rw-r--r--services/java/com/android/server/dreams/DreamController.java280
-rw-r--r--services/java/com/android/server/dreams/DreamManagerService.java393
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java145
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java5
13 files changed, 615 insertions, 577 deletions
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 7aee644..eec19cb 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -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);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index cc2c002..58372f4 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -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
@@ -495,6 +495,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>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
diff --git a/core/java/android/service/dreams/Dream.java b/core/java/android/service/dreams/Dream.java
index 473414c..590acfa 100644
--- a/core/java/android/service/dreams/Dream.java
+++ b/core/java/android/service/dreams/Dream.java
@@ -72,6 +72,12 @@ public class Dream extends Service implements Window.Callback {
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.
*
* @see 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");
}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index bd1c524..1c1b390 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -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);
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
index 89d4ef7..bd87238 100644
--- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -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.");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e6aa632..d72632f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -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()
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 8ad5a91..4a8bf72 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -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,42 +183,14 @@ 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);
- 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;
+ // Release the wake lock that was acquired when the message was posted.
+ mWakeLock.release();
+ }
}
private final Handler mHandler = new Handler(true /*async*/) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 10f6961..738e19b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -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);
}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 85a6e41..07e8f18 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -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);
diff --git a/services/java/com/android/server/dreams/DreamController.java b/services/java/com/android/server/dreams/DreamController.java
index a6ed5ab..81c80187 100644
--- a/services/java/com/android/server/dreams/DreamController.java
+++ b/services/java/com/android/server/dreams/DreamController.java
@@ -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) {
- if (mHandler== null || pw == null) {
- return;
+ 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");
}
- 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));
+ public void startDream(Binder token, ComponentName name, boolean isTest, int userId) {
+ stopDream();
- if (mCurrentDreamComponent != null ) {
- if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) {
- if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream);
- return;
- }
- // stop the current dream before starting a new one
- stop();
- }
+ Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId);
- mCurrentDreamComponent = dream;
- mCurrentDreamIsTest = isTest;
- mCurrentDreamToken = new Binder();
+ mCurrentDream = new DreamRecord(token, name, isTest, userId);
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();
+ mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Unable to add window token for dream.", 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();
+ 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;
+ }
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Unable to bind dream service: " + intent, ex);
+ stopDream();
return;
}
- if (DEBUG) Slog.v(TAG, "Bound service");
- }
- public void attach(ComponentName name, IBinder dream) {
- if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream));
- mCurrentDream = IDreamService.Stub.asInterface(dream);
+ mCurrentDream.mBound = true;
+ }
- 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();
+ final DreamRecord oldDream = mCurrentDream;
+ mCurrentDream = null;
+ Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
+ + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId);
+
+ if (oldDream.mSentStartBroadcast) {
+ mContext.sendBroadcast(mDreamingStoppedIntent);
}
- }
- public void stop() {
- if (DEBUG) Slog.v(TAG, "stop()");
+ 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 (mCurrentDream != null) {
- unlinkDeathRecipient(mCurrentDream.asBinder());
+ if (oldDream.mBound) {
+ mContext.unbindService(oldDream);
+ }
- if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream);
- mContext.unbindService(mServiceConnection);
+ try {
+ mIWindowManager.removeWindowToken(oldDream.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Error removing window token for dream.", ex);
}
- if (mCurrentDreamToken != null) {
- removeWindowToken(mCurrentDreamToken);
+
+ 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;
}
- final boolean wasTest = mCurrentDreamIsTest;
- mCurrentDream = null;
- mCurrentDreamToken = null;
- mCurrentDreamComponent = null;
- mCurrentDreamIsTest = false;
+ mCurrentDream.mService = service;
- if (mListener != null && mHandler != null) {
- mHandler.post(new Runnable(){
- @Override
- public void run() {
- mListener.onDreamStopped(wasTest);
- }});
+ if (!mCurrentDream.mIsTest) {
+ mContext.sendBroadcast(mDreamingStartedIntent);
+ mCurrentDream.mSentStartBroadcast = true;
}
}
- 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();
- }
+ /**
+ * Callback interface to be implemented by the {@link DreamManagerService}.
+ */
+ public interface Listener {
+ void onDreamStopped(Binder token);
}
- 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 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;
}
- }
- 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;
+ // May be called on any thread.
+ @Override
+ public void binderDied() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mService = null;
+ if (mCurrentDream == DreamRecord.this) {
+ stopDream();
+ }
+ }
+ });
}
- }
- private void unlinkDeathRecipient(IBinder dream) {
- if (DEBUG) Slog.v(TAG, "Unlinking death recipient");
- try {
- dream.unlinkToDeath(mDeathRecipient, 0);
- } catch (NoSuchElementException e) {
- // we tried
+ // 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();
+ }
+ }
+ });
+ }
+ }
} \ No newline at end of file
diff --git a/services/java/com/android/server/dreams/DreamManagerService.java b/services/java/com/android/server/dreams/DreamManagerService.java
index 71df247..1f40176 100644
--- a/services/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/java/com/android/server/dreams/DreamManagerService.java
@@ -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 CurrentUserManager mCurrentUserManager = new CurrentUserManager();
-
- 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 final Context mContext;
+ private final DreamHandler mHandler;
+ private final DreamController mController;
+ private final PowerManager mPowerManager;
- private boolean mIsDreaming;
+ private Binder mCurrentDreamToken;
+ private ComponentName mCurrentDreamName;
+ private int mCurrentDreamUserId;
+ private boolean mCurrentDreamIsTest;
- 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();
+ /**
+ * 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);
+ }
+ }
}
- // 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);
+ /**
+ * Called by the power manager to stop a dream.
+ */
+ public void stopDream() {
+ synchronized (mLock) {
+ stopDreamLocked();
}
}
- 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);
- }
- }
- mIsDreaming = isDreaming;
+ 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;
- }
-
- 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);
- }});
+ private final class DreamHandler extends Handler {
+ public DreamHandler(Looper looper) {
+ super(looper, null, true /*async*/);
}
-
- public void requestStopSelf(final IBinder token) {
- post(new Runnable(){
- @Override
- public void run() {
- mController.stopSelf(token);
- }});
- }
-
- public void requestStop() {
- post(mStopRunnable);
- }
-
}
-
}
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 030eb5e..ad138e8 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -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();
}
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 0c85204..0cf0f21 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -60,6 +60,11 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
+ public void nap(long arg0) throws RemoteException {
+ // pass for now.
+ }
+
+ @Override
public void preventScreenOn(boolean arg0) throws RemoteException {
// pass for now.
}