summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2016-09-06 16:45:46 -0700
committer Adrian Roos <roosa@google.com> 2016-09-08 20:06:48 +0000
commit7445c0bb86c57172eb88a4e37183634abce2e37b (patch)
tree1e18a92c3ee14fabd0652ccffdeb30de60289fab
parenta2153e6246138d303fe824d99aadf28886767743 (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
-rw-r--r--core/java/android/os/PowerManager.java29
-rw-r--r--core/java/android/service/dreams/DreamService.java160
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java33
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java10
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) {