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);