diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-09-26 17:25:22 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-09-26 17:25:23 -0700 |
commit | 0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb (patch) | |
tree | 128f62103b9db114d27037404d9cef29d44b3d4f | |
parent | 5a864aa0d8919d38b23c9e7e90455181d3a979e3 (diff) | |
parent | 62c82e4d92cc0b856059f905d81885f7808a0e7d (diff) | |
download | frameworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.zip frameworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.tar.gz frameworks_base-0c9bbf4e13f2be6a4b930da2379c77e06a68c2cb.tar.bz2 |
Merge "Make DreamManagerService more robust." into jb-mr1-dev
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. } |