Make activity manager sleep top activity when dreaming.
Added the concept of a sleep token to represent a demand to sleep.
We grab this token whenever a dream window is visible.
This change also deletes a special condition in shouldSleepLocked()
which would tend to keep activities running when the lock screen
was shown unless they had previously been sleeping. I believe this
condition is no longer necessary and the policy is much simpler
without it. Let's see if anything breaks.
NOTE: This can currently only be used within the system server process
but it might make sense to move it to IActivityManager (guarded by
DEVICE_POWER permission) so that keyguard can use it. If we added
a callback on release to inform the client that the underlying activity
has finished drawing then we could untangle a bunch of keyguard
specific logic.
Bug: 18866521
Change-Id: I84b2118f5b990a71a1647d1cee536ad3d99f3a24
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d56dc1e..bde8f39 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,6 +16,8 @@
package android.app;
+import android.annotation.NonNull;
+
/**
* Activity manager local system service interface.
*
@@ -27,4 +29,23 @@
public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
String processName, String abiOverride, int uid, Runnable crashHandler);
+
+ /**
+ * Acquires a sleep token with the specified tag.
+ *
+ * @param tag A string identifying the purpose of the token (eg. "Dream").
+ */
+ public abstract SleepToken acquireSleepToken(@NonNull String tag);
+
+ /**
+ * Sleep tokens cause the activity manager to put the top activity to sleep.
+ * They are used by components such as dreams that may hide and block interaction
+ * with underlying activities.
+ */
+ public static abstract class SleepToken {
+ /**
+ * Releases the sleep token.
+ */
+ public abstract void release();
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f0e35..8e5ffe2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -110,6 +110,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -1009,6 +1010,13 @@
*/
private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ /**
+ * A list of tokens that cause the top activity to be put to sleep.
+ * They are used by components that may hide and block interaction with underlying
+ * activities.
+ */
+ final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+
static final int LOCK_SCREEN_HIDDEN = 0;
static final int LOCK_SCREEN_LEAVING = 1;
static final int LOCK_SCREEN_SHOWN = 2;
@@ -9810,15 +9818,14 @@
return false;
}
+ // TODO: Transform the lock screen state into a sleep token instead.
switch (mWakefulness) {
case PowerManagerInternal.WAKEFULNESS_AWAKE:
case PowerManagerInternal.WAKEFULNESS_DREAMING:
- // If we're interactive but applications are already paused then defer
- // resuming them until the lock screen is hidden.
- return mSleeping && mLockScreenShown != LOCK_SCREEN_HIDDEN;
case PowerManagerInternal.WAKEFULNESS_DOZING:
- // If we're dozing then pause applications whenever the lock screen is shown.
- return mLockScreenShown != LOCK_SCREEN_HIDDEN;
+ // Pause applications whenever the lock screen is shown or any sleep
+ // tokens have been acquired.
+ return (mLockScreenShown != LOCK_SCREEN_HIDDEN || !mSleepTokens.isEmpty());
case PowerManagerInternal.WAKEFULNESS_ASLEEP:
default:
// If we're asleep then pause applications unconditionally.
@@ -12947,6 +12954,7 @@
if (dumpPackage == null) {
pw.println(" mWakefulness="
+ PowerManagerInternal.wakefulnessToString(mWakefulness));
+ pw.println(" mSleepTokens=" + mSleepTokens);
pw.println(" mSleeping=" + mSleeping + " mLockScreenShown="
+ lockScreenShownToString());
pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode);
@@ -19720,6 +19728,42 @@
return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs,
processName, abiOverride, uid, crashHandler);
}
+
+ @Override
+ public SleepToken acquireSleepToken(String tag) {
+ Preconditions.checkNotNull(tag);
+
+ synchronized (ActivityManagerService.this) {
+ SleepTokenImpl token = new SleepTokenImpl(tag);
+ mSleepTokens.add(token);
+ updateSleepIfNeededLocked();
+ return token;
+ }
+ }
+ }
+
+ private final class SleepTokenImpl extends SleepToken {
+ private final String mTag;
+ private final long mAcquireTime;
+
+ public SleepTokenImpl(String tag) {
+ mTag = tag;
+ mAcquireTime = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public void release() {
+ synchronized (ActivityManagerService.this) {
+ if (mSleepTokens.remove(this)) {
+ updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{\"" + mTag + "\", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+ }
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936840a..fbacd75 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -17,6 +17,8 @@
package com.android.server.policy;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IUiModeManager;
@@ -251,6 +253,7 @@
WindowManagerFuncs mWindowManagerFuncs;
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
+ ActivityManagerInternal mActivityManagerInternal;
DreamManagerInternal mDreamManagerInternal;
IStatusBarService mStatusBarService;
boolean mPreloadedRecentApps;
@@ -492,6 +495,8 @@
boolean mShowingLockscreen;
boolean mShowingDream;
boolean mDreamingLockscreen;
+ boolean mDreamingSleepTokenNeeded;
+ SleepToken mDreamingSleepToken;
boolean mKeyguardSecure;
boolean mKeyguardSecureIncludingHidden;
volatile boolean mKeyguardOccluded;
@@ -598,6 +603,7 @@
private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
private static final int MSG_POWER_DELAYED_PRESS = 13;
private static final int MSG_POWER_LONG_PRESS = 14;
+ private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
private class PolicyHandler extends Handler {
@Override
@@ -646,6 +652,9 @@
case MSG_POWER_LONG_PRESS:
powerLongPress();
break;
+ case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
+ updateDreamingSleepToken(msg.arg1 != 0);
+ break;
}
}
}
@@ -1219,6 +1228,7 @@
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
// Init display burn-in protection
@@ -4235,6 +4245,15 @@
// while the dream is showing.
if (!mShowingDream) {
mDreamingLockscreen = mShowingLockscreen;
+ if (mDreamingSleepTokenNeeded) {
+ mDreamingSleepTokenNeeded = false;
+ mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
+ }
+ } else {
+ if (!mDreamingSleepTokenNeeded) {
+ mDreamingSleepTokenNeeded = true;
+ mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
+ }
}
if (mStatusBar != null) {
@@ -5847,6 +5866,18 @@
}
}
+ private void updateDreamingSleepToken(boolean acquire) {
+ if (acquire) {
+ if (mDreamingSleepToken == null) {
+ mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken("Dream");
+ }
+ } else {
+ if (mDreamingSleepToken != null) {
+ mDreamingSleepToken.release();
+ }
+ }
+ }
+
/** {@inheritDoc} */
@Override
public void enableScreenAfterBoot() {
@@ -6483,7 +6514,8 @@
pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
pw.print(prefix); pw.print("mShowingLockscreen="); pw.print(mShowingLockscreen);
pw.print(" mShowingDream="); pw.print(mShowingDream);
- pw.print(" mDreamingLockscreen="); pw.println(mDreamingLockscreen);
+ pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
+ pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
if (mLastInputMethodWindow != null) {
pw.print(prefix); pw.print("mLastInputMethodWindow=");
pw.println(mLastInputMethodWindow);