diff options
9 files changed, 185 insertions, 84 deletions
diff --git a/api/current.txt b/api/current.txt index 399e92c..86ea9c8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26961,6 +26961,7 @@ package android.service.dreams { method public void onPanelClosed(int, android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); method public boolean onSearchRequested(); + method public void onWakeUp(); method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); method public void onWindowFocusChanged(boolean); method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback); @@ -26970,6 +26971,7 @@ package android.service.dreams { method public void setFullscreen(boolean); method public void setInteractive(boolean); method public void setScreenBright(boolean); + method public final void wakeUp(); field public static final java.lang.String DREAM_META_DATA = "android.service.dream"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.DreamService"; } diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index 9f7ddba..ff7cef9 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -24,13 +24,19 @@ package android.service.dreams; public abstract class DreamManagerInternal { /** * Called by the power manager to start a dream. + * + * @param doze If true, starts the doze dream component if one has been configured, + * otherwise starts the user-specified dream. */ public abstract void startDream(boolean doze); /** * Called by the power manager to stop a dream. + * + * @param immediate If true, ends the dream summarily, otherwise gives it some time + * to perform a proper exit transition. */ - public abstract void stopDream(); + public abstract void stopDream(boolean immediate); /** * Called by the power manager to determine whether a dream is running. diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 1e9ce05..5fa542a 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -171,6 +171,7 @@ public class DreamService extends Service implements Window.Callback { private boolean mFullscreen; private boolean mScreenBright = true; private boolean mStarted; + private boolean mWaking; private boolean mFinished; private boolean mCanDoze; private boolean mDozing; @@ -196,12 +197,12 @@ public class DreamService extends Service implements Window.Callback { public boolean dispatchKeyEvent(KeyEvent event) { // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK if (!mInteractive) { - if (mDebug) Slog.v(TAG, "Finishing on keyEvent"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on keyEvent"); + wakeUp(); return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - if (mDebug) Slog.v(TAG, "Finishing on back key"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on back key"); + wakeUp(); return true; } return mWindow.superDispatchKeyEvent(event); @@ -211,8 +212,8 @@ public class DreamService extends Service implements Window.Callback { @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (!mInteractive) { - if (mDebug) Slog.v(TAG, "Finishing on keyShortcutEvent"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on keyShortcutEvent"); + wakeUp(); return true; } return mWindow.superDispatchKeyShortcutEvent(event); @@ -224,8 +225,8 @@ public class DreamService extends Service implements Window.Callback { // TODO: create more flexible version of mInteractive that allows clicks // but finish()es on any other kind of activity if (!mInteractive) { - if (mDebug) Slog.v(TAG, "Finishing on touchEvent"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on touchEvent"); + wakeUp(); return true; } return mWindow.superDispatchTouchEvent(event); @@ -235,8 +236,8 @@ public class DreamService extends Service implements Window.Callback { @Override public boolean dispatchTrackballEvent(MotionEvent event) { if (!mInteractive) { - if (mDebug) Slog.v(TAG, "Finishing on trackballEvent"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on trackballEvent"); + wakeUp(); return true; } return mWindow.superDispatchTrackballEvent(event); @@ -246,8 +247,8 @@ public class DreamService extends Service implements Window.Callback { @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { if (!mInteractive) { - if (mDebug) Slog.v(TAG, "Finishing on genericMotionEvent"); - safelyFinish(); + if (mDebug) Slog.v(TAG, "Waking up on genericMotionEvent"); + wakeUp(); return true; } return mWindow.superDispatchGenericMotionEvent(event); @@ -690,6 +691,22 @@ public class DreamService extends Service implements Window.Callback { // hook for subclasses } + /** + * Called when the dream is being asked to stop itself and wake. + * <p> + * The default implementation simply calls {@link #finish} which ends the dream + * immediately. Subclasses may override this function to perform a smooth exit + * transition then call {@link #finish} afterwards. + * </p><p> + * Note that the dream will only be given a short period of time (currently about + * five seconds) to wake up. If the dream does not finish itself in a timely manner + * then the system will forcibly finish it once the time allowance is up. + * </p> + */ + public void onWakeUp() { + finish(); + } + /** {@inheritDoc} */ @Override public final IBinder onBind(Intent intent) { @@ -705,8 +722,62 @@ public class DreamService extends Service implements Window.Callback { * </p> */ public final void finish() { - if (mDebug) Slog.v(TAG, "finish()"); - finishInternal(); + if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished); + + if (!mFinished) { + mFinished = true; + + if (mWindowToken == null) { + Slog.w(TAG, "Finish was called before the dream was attached."); + } else { + try { + mSandman.finishSelf(mWindowToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died + } + } + + stopSelf(); // if launched via any other means + } + } + + /** + * Wakes the dream up gently. + * <p> + * Calls {@link #onWakeUp} to give the dream a chance to perform an exit transition. + * When the transition is over, the dream should call {@link #finish}. + * </p> + */ + public final void wakeUp() { + wakeUp(false); + } + + private void wakeUp(boolean fromSystem) { + if (mDebug) Slog.v(TAG, "wakeUp(): fromSystem=" + fromSystem + + ", mWaking=" + mWaking + ", mFinished=" + mFinished); + + if (!mWaking && !mFinished) { + mWaking = true; + + // As a minor optimization, invoke the callback first in case it simply + // calls finish() immediately so there wouldn't be much point in telling + // the system that we are finishing the dream gently. + onWakeUp(); + + // Now tell the system we are waking gently, unless we already told + // it we were finishing immediately. + if (!fromSystem && !mFinished) { + if (mWindowToken == null) { + Slog.w(TAG, "WakeUp was called before the dream was attached."); + } else { + try { + mSandman.finishSelf(mWindowToken, false /*immediate*/); + } catch (RemoteException ex) { + // system server died + } + } + } + } } /** {@inheritDoc} */ @@ -763,10 +834,10 @@ public class DreamService extends Service implements Window.Callback { Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); return; } - if (mFinished) { + if (mFinished || mWaking) { Slog.w(TAG, "attach() called after dream already finished"); try { - mSandman.finishSelf(windowToken); + mSandman.finishSelf(windowToken, true /*immediate*/); } catch (RemoteException ex) { // system server died } @@ -837,37 +908,6 @@ public class DreamService extends Service implements Window.Callback { }); } - private void safelyFinish() { - if (mDebug) Slog.v(TAG, "safelyFinish()"); - - finish(); - - if (!mFinished) { - Slog.w(TAG, "Bad dream, did not call super.finish()"); - finishInternal(); - } - } - - private void finishInternal() { - if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); - - if (!mFinished) { - mFinished = true; - - if (mWindowToken == null) { - Slog.w(TAG, "Finish was called before the dream was attached."); - } else { - try { - mSandman.finishSelf(mWindowToken); - } catch (RemoteException ex) { - // system server died - } - } - - stopSelf(); // if launched via any other means - } - } - private boolean getWindowFlagValue(int flag, boolean defaultValue) { return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; } @@ -946,6 +986,15 @@ public class DreamService extends Service implements Window.Callback { } }); } - } + @Override + public void wakeUp() { + mHandler.post(new Runnable() { + @Override + public void run() { + DreamService.this.wakeUp(true /*fromSystem*/); + } + }); + } + } } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 2718e31..9608a4d 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -31,7 +31,7 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); - void finishSelf(in IBinder token); + void finishSelf(in IBinder token, boolean immediate); void startDozing(in IBinder token); void stopDozing(in IBinder token); IDozeHardware getDozeHardware(in IBinder token); diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl index bd58f1d..9bb1804 100644 --- a/core/java/android/service/dreams/IDreamService.aidl +++ b/core/java/android/service/dreams/IDreamService.aidl @@ -22,4 +22,5 @@ package android.service.dreams; oneway interface IDreamService { void attach(IBinder windowToken, boolean canDoze); void detach(); + void wakeUp(); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index a3c84c6..ba4c588 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -2179,7 +2179,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If there's a dream running then use home to escape the dream // but don't actually go home. if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) { - mDreamManagerInternal.stopDream(); + mDreamManagerInternal.stopDream(false /*immediate*/); return -1; } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 649b5c9..334f0ac 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -47,6 +47,9 @@ final class DreamController { // How long we wait for a newly bound dream to create the service connection private static final int DREAM_CONNECTION_TIMEOUT = 5 * 1000; + // Time to allow the dream to perform an exit transition when waking up. + private static final int DREAM_FINISH_TIMEOUT = 5 * 1000; + private final Context mContext; private final Handler mHandler; private final Listener mListener; @@ -66,11 +69,19 @@ final class DreamController { public void run() { if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) { Slog.w(TAG, "Bound dream did not connect in the time allotted"); - stopDream(); + stopDream(true /*immediate*/); } } }; + private final Runnable mStopStubbornDreamRunnable = new Runnable() { + @Override + public void run() { + Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); + stopDream(true /*immediate*/); + } + }; + public DreamController(Context context, Handler handler, Listener listener) { mContext = context; mHandler = handler; @@ -90,6 +101,7 @@ final class DreamController { pw.println(" mBound=" + mCurrentDream.mBound); pw.println(" mService=" + mCurrentDream.mService); pw.println(" mSentStartBroadcast=" + mCurrentDream.mSentStartBroadcast); + pw.println(" mWakingGently=" + mCurrentDream.mWakingGently); } else { pw.println(" mCurrentDream: null"); } @@ -97,7 +109,7 @@ final class DreamController { public void startDream(Binder token, ComponentName name, boolean isTest, boolean canDoze, int userId) { - stopDream(); + stopDream(true /*immediate*/); // Close the notification shade. Don't need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); @@ -112,7 +124,7 @@ final class DreamController { mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); } catch (RemoteException ex) { Slog.e(TAG, "Unable to add window token for dream.", ex); - stopDream(); + stopDream(true /*immediate*/); return; } @@ -123,12 +135,12 @@ final class DreamController { if (!mContext.bindServiceAsUser(intent, mCurrentDream, Context.BIND_AUTO_CREATE, new UserHandle(userId))) { Slog.e(TAG, "Unable to bind dream service: " + intent); - stopDream(); + stopDream(true /*immediate*/); return; } } catch (SecurityException ex) { Slog.e(TAG, "Unable to bind dream service: " + intent, ex); - stopDream(); + stopDream(true /*immediate*/); return; } @@ -136,11 +148,29 @@ final class DreamController { mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT); } - public void stopDream() { + public void stopDream(boolean immediate) { if (mCurrentDream == null) { return; } + if (!immediate) { + if (mCurrentDream.mWakingGently) { + return; // already waking gently + } + + if (mCurrentDream.mService != null) { + // Give the dream a moment to wake up and finish itself gently. + mCurrentDream.mWakingGently = true; + try { + mCurrentDream.mService.wakeUp(); + mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT); + return; + } catch (RemoteException ex) { + // oh well, we tried, finish immediately instead + } + } + } + final DreamRecord oldDream = mCurrentDream; mCurrentDream = null; Slog.i(TAG, "Stopping dream: name=" + oldDream.mName @@ -148,6 +178,7 @@ final class DreamController { + ", userId=" + oldDream.mUserId); mHandler.removeCallbacks(mStopUnconnectedDreamRunnable); + mHandler.removeCallbacks(mStopStubbornDreamRunnable); if (oldDream.mSentStartBroadcast) { mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL); @@ -195,7 +226,7 @@ final class DreamController { service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze); } catch (RemoteException ex) { Slog.e(TAG, "The dream service died unexpectedly.", ex); - stopDream(); + stopDream(true /*immediate*/); return; } @@ -226,6 +257,8 @@ final class DreamController { public IDreamService mService; public boolean mSentStartBroadcast; + public boolean mWakingGently; + public DreamRecord(Binder token, ComponentName name, boolean isTest, boolean canDoze, int userId) { mToken = token; @@ -243,7 +276,7 @@ final class DreamController { public void run() { mService = null; if (mCurrentDream == DreamRecord.this) { - stopDream(); + stopDream(true /*immediate*/); } } }); @@ -271,7 +304,7 @@ final class DreamController { public void run() { mService = null; if (mCurrentDream == DreamRecord.this) { - stopDream(); + stopDream(true /*immediate*/); } } }); diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index a35e2ba..4ccf73b 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -80,6 +80,7 @@ public final class DreamManagerService extends SystemService { private boolean mCurrentDreamIsTest; private boolean mCurrentDreamCanDoze; private boolean mCurrentDreamIsDozing; + private boolean mCurrentDreamIsWaking; private DozeHardwareWrapper mCurrentDreamDozeHardware; public DreamManagerService(Context context) { @@ -113,7 +114,7 @@ public final class DreamManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { - stopDreamLocked(); + stopDreamLocked(false /*immediate*/); } } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); @@ -132,6 +133,7 @@ public final class DreamManagerService extends SystemService { pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze); pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing); + pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking); pw.println("mCurrentDreamDozeHardware=" + mCurrentDreamDozeHardware); pw.println("getDozeComponent()=" + getDozeComponent()); pw.println(); @@ -146,7 +148,8 @@ public final class DreamManagerService extends SystemService { private boolean isDreamingInternal() { synchronized (mLock) { - return mCurrentDreamToken != null && !mCurrentDreamIsTest; + return mCurrentDreamToken != null && !mCurrentDreamIsTest + && !mCurrentDreamIsWaking; } } @@ -166,12 +169,12 @@ public final class DreamManagerService extends SystemService { // for example when being undocked. long time = SystemClock.uptimeMillis(); mPowerManager.userActivity(time, false /*noChangeLights*/); - stopDreamInternal(); + stopDreamInternal(false /*immediate*/); } - private void finishSelfInternal(IBinder token) { + private void finishSelfInternal(IBinder token, boolean immediate) { if (DEBUG) { - Slog.d(TAG, "Dream finished: " + token); + Slog.d(TAG, "Dream finished: " + token + ", immediate=" + immediate); } // Note that a dream finishing and self-terminating is not @@ -183,7 +186,7 @@ public final class DreamManagerService extends SystemService { // device may simply go to sleep. synchronized (mLock) { if (mCurrentDreamToken == token) { - stopDreamLocked(); + stopDreamLocked(immediate); } } } @@ -204,9 +207,9 @@ public final class DreamManagerService extends SystemService { } } - private void stopDreamInternal() { + private void stopDreamInternal(boolean immediate) { synchronized (mLock) { - stopDreamLocked(); + stopDreamLocked(immediate); } } @@ -345,9 +348,9 @@ public final class DreamManagerService extends SystemService { return; } - stopDreamLocked(); + stopDreamLocked(true /*immediate*/); - if (DEBUG) Slog.i(TAG, "Entering dreamland."); + Slog.i(TAG, "Entering dreamland."); final Binder newToken = new Binder(); mCurrentDreamToken = newToken; @@ -364,16 +367,22 @@ public final class DreamManagerService extends SystemService { }); } - private void stopDreamLocked() { + private void stopDreamLocked(final boolean immediate) { if (mCurrentDreamToken != null) { - if (DEBUG) Slog.i(TAG, "Leaving dreamland."); - - cleanupDreamLocked(); + if (immediate) { + Slog.i(TAG, "Leaving dreamland."); + cleanupDreamLocked(); + } else if (mCurrentDreamIsWaking) { + return; // already waking + } else { + Slog.i(TAG, "Gently waking up from dream."); + mCurrentDreamIsWaking = true; + } mHandler.post(new Runnable() { @Override public void run() { - mController.stopDream(); + mController.stopDream(immediate); } }); } @@ -385,6 +394,7 @@ public final class DreamManagerService extends SystemService { mCurrentDreamIsTest = false; mCurrentDreamCanDoze = false; mCurrentDreamUserId = 0; + mCurrentDreamIsWaking = false; if (mCurrentDreamIsDozing) { mCurrentDreamIsDozing = false; mDozeWakeLock.release(); @@ -568,7 +578,7 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call - public void finishSelf(IBinder token) { + public void finishSelf(IBinder token, boolean immediate) { // Requires no permission, called by Dream from an arbitrary process. if (token == null) { throw new IllegalArgumentException("token must not be null"); @@ -576,7 +586,7 @@ public final class DreamManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { - finishSelfInternal(token); + finishSelfInternal(token, immediate); } finally { Binder.restoreCallingIdentity(ident); } @@ -635,8 +645,8 @@ public final class DreamManagerService extends SystemService { } @Override - public void stopDream() { - stopDreamInternal(); + public void stopDream(boolean immediate) { + stopDreamInternal(immediate); } @Override @@ -683,11 +693,11 @@ public final class DreamManagerService extends SystemService { @Override public void run() { if (DEBUG) Slog.d(TAG, "System properties changed"); - synchronized(mLock) { + synchronized (mLock) { if (mCurrentDreamName != null && mCurrentDreamCanDoze && !mCurrentDreamName.equals(getDozeComponent())) { - // may have updated the doze component, wake up - stopDreamLocked(); + // May have updated the doze component, wake up + mPowerManager.wakeUp(SystemClock.uptimeMillis()); } } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cb4d339..4b1e8eb 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1551,7 +1551,7 @@ public final class PowerManagerService extends com.android.server.SystemService if (mDreamManager != null) { // Restart the dream whenever the sandman is summoned. if (startDreaming) { - mDreamManager.stopDream(); + mDreamManager.stopDream(false /*immediate*/); mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING); } isDreaming = mDreamManager.isDreaming(); @@ -1619,7 +1619,7 @@ public final class PowerManagerService extends com.android.server.SystemService // Stop dream. if (isDreaming) { - mDreamManager.stopDream(); + mDreamManager.stopDream(false /*immediate*/); } } |