diff options
| author | 2016-09-06 16:45:46 -0700 | |
|---|---|---|
| committer | 2016-09-08 20:06:48 +0000 | |
| commit | 7445c0bb86c57172eb88a4e37183634abce2e37b (patch) | |
| tree | 1e18a92c3ee14fabd0652ccffdeb30de60289fab | |
| parent | a2153e6246138d303fe824d99aadf28886767743 (diff) | |
Hold WakeLock while DreamService starts
Fixes a bug where DozeService would not properly initialize
because the CPU went to sleep before onDreamingStarted completed,
causing the pickup gesture to not work.
Change-Id: I85955a2b7d6bad5171accbc336117a9660b1b198
Fixes: 31044352
4 files changed, 151 insertions, 81 deletions
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index ce7a1243fe7a..1aed9b322816 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1358,5 +1358,34 @@ public final class PowerManager { + " held=" + mHeld + ", refCount=" + mCount + "}"; } } + + /** + * Wraps a Runnable such that this method immediately acquires the wake lock and then + * once the Runnable is done the wake lock is released. + * + * <p>Example: + * + * <pre> + * mHandler.post(mWakeLock.wrap(() -> { + * // do things on handler, lock is held while we're waiting for this + * // to get scheduled and until the runnable is done executing. + * }); + * </pre> + * + * <p>Note: you must make sure that the Runnable eventually gets executed, otherwise you'll + * leak the wakelock! + * + * @hide + */ + public Runnable wrap(Runnable r) { + acquire(); + return () -> { + try { + r.run(); + } finally { + release(); + } + }; + } } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 0557d138eb74..e958fbef563d 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -192,6 +192,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private PowerManager.WakeLock mWakeLock; + private boolean mWakeLockAcquired; + public DreamService() { mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } @@ -786,6 +789,8 @@ public class DreamService extends Service implements Window.Callback { public void onCreate() { if (mDebug) Slog.v(TAG, "onCreate()"); super.onCreate(); + mWakeLock = getSystemService(PowerManager.class) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DreamService"); } /** @@ -825,9 +830,21 @@ public class DreamService extends Service implements Window.Callback { @Override public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); + + // Need to stay awake until we dispatch onDreamingStarted. This is released either in + // attach() or onDestroy(). + mWakeLock.acquire(5000); + mWakeLockAcquired = true; return new DreamServiceWrapper(); } + private void releaseWakeLockIfNeeded() { + if (mWakeLockAcquired) { + mWakeLock.release(); + mWakeLockAcquired = false; + } + } + /** * Stops the dream and detaches from the window. * <p> @@ -904,6 +921,8 @@ public class DreamService extends Service implements Window.Callback { detach(); super.onDestroy(); + + releaseWakeLockIfNeeded(); // for acquire in onBind() } // end public api @@ -944,83 +963,88 @@ public class DreamService extends Service implements Window.Callback { * @param windowToken A window token that will allow a window to be created in the correct layer. */ private final void attach(IBinder windowToken, boolean canDoze) { - if (mWindowToken != null) { - Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); - return; - } - if (mFinished || mWaking) { - Slog.w(TAG, "attach() called after dream already finished"); - try { - mSandman.finishSelf(windowToken, true /*immediate*/); - } catch (RemoteException ex) { - // system server died + try { + if (mWindowToken != null) { + Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); + return; + } + if (mFinished || mWaking) { + Slog.w(TAG, "attach() called after dream already finished"); + try { + mSandman.finishSelf(windowToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died + } + return; } - return; - } - - mWindowToken = windowToken; - mCanDoze = canDoze; - if (mWindowless && !mCanDoze) { - throw new IllegalStateException("Only doze dreams can be windowless"); - } - if (!mWindowless) { - mWindow = new PhoneWindow(this); - mWindow.setCallback(this); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - mWindow.setFormat(PixelFormat.OPAQUE); - - if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", - windowToken, WindowManager.LayoutParams.TYPE_DREAM)); - WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_DREAM; - lp.token = windowToken; - lp.windowAnimations = com.android.internal.R.style.Animation_Dream; - lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) - | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) - ); - mWindow.setAttributes(lp); - // Workaround: Currently low-profile and in-window system bar backgrounds don't go - // along well. Dreams usually don't need such bars anyways, so disable them by default. - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - mWindow.setWindowManager(null, windowToken, "dream", true); + mWindowToken = windowToken; + mCanDoze = canDoze; + if (mWindowless && !mCanDoze) { + throw new IllegalStateException("Only doze dreams can be windowless"); + } + if (!mWindowless) { + mWindow = new PhoneWindow(this); + mWindow.setCallback(this); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); + mWindow.setFormat(PixelFormat.OPAQUE); + + if (mDebug) { + Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); + } - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_DREAM; + lp.token = windowToken; + lp.windowAnimations = com.android.internal.R.style.Animation_Dream; + lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) + | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) + ); + mWindow.setAttributes(lp); + // Workaround: Currently low-profile and in-window system bar backgrounds don't go + // along well. Dreams usually don't need such bars anyways, so disable them by default. + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + mWindow.setWindowManager(null, windowToken, "dream", true); + + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); - try { - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (WindowManager.BadTokenException ex) { - // This can happen because the dream manager service will remove the token - // immediately without necessarily waiting for the dream to start. - // We should receive a finish message soon. - Slog.i(TAG, "attach() called after window token already removed, dream will " - + "finish soon"); - mWindow = null; - return; + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (WindowManager.BadTokenException ex) { + // This can happen because the dream manager service will remove the token + // immediately without necessarily waiting for the dream to start. + // We should receive a finish message soon. + Slog.i(TAG, "attach() called after window token already removed, dream will " + + "finish soon"); + mWindow = null; + return; + } } - } - // We need to defer calling onDreamingStarted until after onWindowAttached, - // which is posted to the handler by addView, so we post onDreamingStarted - // to the handler also. Need to watch out here in case detach occurs before - // this callback is invoked. - mHandler.post(new Runnable() { - @Override - public void run() { + // We need to defer calling onDreamingStarted until after onWindowAttached, + // which is posted to the handler by addView, so we post onDreamingStarted + // to the handler also. Need to watch out here in case detach occurs before + // this callback is invoked. + mHandler.post(mWakeLock.wrap(() -> { if (mWindow != null || mWindowless) { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + if (mDebug) { + Slog.v(TAG, "Calling onDreamingStarted()"); + } mStarted = true; onDreamingStarted(); } - } - }); + })); + } finally { + releaseWakeLockIfNeeded(); // for acquire in onBind + } } private boolean getWindowFlagValue(int flag, boolean defaultValue) { diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index a63399656bab..9fa93f4b8a6c 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -26,6 +26,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.os.IBinder.DeathRecipient; import android.os.SystemClock; @@ -116,19 +117,19 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { stopDream(true /*immediate*/); Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); try { - // Close the notification shade. Don't need to send to all, but better to be explicit. + // Close the notification shade. No need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", canDoze=" + canDoze + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); + mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, @@ -230,6 +231,7 @@ final class DreamController { if (oldDream.mBound) { mContext.unbindService(oldDream); } + oldDream.releaseWakeLockIfNeeded(); try { mIWindowManager.removeWindowToken(oldDream.mToken); @@ -280,6 +282,7 @@ final class DreamController { public final boolean mCanDoze; public final int mUserId; + public PowerManager.WakeLock mWakeLock; public boolean mBound; public boolean mConnected; public IDreamService mService; @@ -288,12 +291,17 @@ final class DreamController { public boolean mWakingGently; public DreamRecord(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { mToken = token; mName = name; mIsTest = isTest; mCanDoze = canDoze; mUserId = userId; + mWakeLock = wakeLock; + // Hold the lock while we're waiting for the service to connect. Released either when + // DreamService connects (and is then responsible for keeping the device awake) or + // dreaming stops. + mWakeLock.acquire(); } // May be called on any thread. @@ -316,14 +324,25 @@ final class DreamController { mHandler.post(new Runnable() { @Override public void run() { - mConnected = true; - if (mCurrentDream == DreamRecord.this && mService == null) { - attach(IDreamService.Stub.asInterface(service)); + try { + mConnected = true; + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + } + } finally { + releaseWakeLockIfNeeded(); } } }); } + private void releaseWakeLockIfNeeded() { + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + } + // May be called on any thread. @Override public void onServiceDisconnected(ComponentName name) { diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index a783fa25ed45..20bccf19f131 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -370,12 +370,10 @@ public final class DreamManagerService extends SystemService { mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; - mHandler.post(new Runnable() { - @Override - public void run() { - mController.startDream(newToken, name, isTest, canDoze, userId); - } - }); + PowerManager.WakeLock wakeLock = mPowerManager + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); + mHandler.post(wakeLock.wrap( + () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock))); } private void stopDreamLocked(final boolean immediate) { |