Delay coming out of doze until keyguard dismissed

v2 (earlier attempt was reverted due to deadlock)

Don't come out of doze when screen is on, but
wait until the keyguard is dismissed as well.
Pass along the keyguard dismissed to DeviceIdle

Request exitIdle when notifications are clicked.

Bug: 63527576
Test: Manual:
       Turn off screen
       adb shell dumpsys battery unplug
       adb shell dumpsys deviceidle step x4
       Turn on screen, verify still in doze
       Unlock, verify out of doze

Change-Id: Ic5129611d4f098723b31d62a27db8f50860697d4
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a12c85a..44974ff 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -141,6 +141,8 @@
     private boolean mHasNetworkLocation;
     private Location mLastGenericLocation;
     private Location mLastGpsLocation;
+    // Current locked state of the screen
+    private boolean mScreenLocked;
 
     /** Device is currently active. */
     private static final int STATE_ACTIVE = 0;
@@ -156,6 +158,7 @@
     private static final int STATE_IDLE = 5;
     /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
     private static final int STATE_IDLE_MAINTENANCE = 6;
+
     private static String stateToString(int state) {
         switch (state) {
             case STATE_ACTIVE: return "ACTIVE";
@@ -547,6 +550,11 @@
                 "sms_temp_app_whitelist_duration";
         private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
                 "notification_whitelist_duration";
+        /**
+         * Whether to wait for the user to unlock the device before causing screen-on to
+         * exit doze. Default = true
+         */
+        private static final String KEY_WAIT_FOR_UNLOCK = "wait_for_unlock";
 
         /**
          * This is the time, after becoming inactive, that we go in to the first
@@ -765,6 +773,8 @@
          */
         public long NOTIFICATION_WHITELIST_DURATION;
 
+        public boolean WAIT_FOR_UNLOCK;
+
         private final ContentResolver mResolver;
         private final boolean mSmallBatteryDevice;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -855,6 +865,7 @@
                         KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
                 NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+                WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, false);
             }
         }
 
@@ -962,6 +973,9 @@
             pw.print("    "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
             TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
             pw.println();
+
+            pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
+            pw.println(WAIT_FOR_UNLOCK);
         }
     }
 
@@ -1340,6 +1354,19 @@
         }
     }
 
+    private ActivityManagerInternal.ScreenObserver mScreenObserver =
+            new ActivityManagerInternal.ScreenObserver() {
+                @Override
+                public void onAwakeStateChanged(boolean isAwake) { }
+
+                @Override
+                public void onKeyguardStateChanged(boolean isShowing) {
+                    synchronized (DeviceIdleController.this) {
+                        DeviceIdleController.this.keyguardShowingLocked(isShowing);
+                    }
+                }
+            };
+
     public DeviceIdleController(Context context) {
         super(context);
         mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
@@ -1406,6 +1433,7 @@
 
             mNetworkConnected = true;
             mScreenOn = true;
+            mScreenLocked = false;
             // Start out assuming we are charging.  If we aren't, we will at least get
             // a battery update the next time the level drops.
             mCharging = true;
@@ -1501,6 +1529,8 @@
                 mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
+                mLocalActivityManager.registerScreenObserver(mScreenObserver);
+
                 passWhiteListToForceAppStandbyTrackerLocked();
                 updateInteractivityLocked();
             }
@@ -1976,7 +2006,7 @@
             }
         } else if (screenOn) {
             mScreenOn = true;
-            if (!mForceIdle) {
+            if (!mForceIdle && (!mScreenLocked || !mConstants.WAIT_FOR_UNLOCK)) {
                 becomeActiveLocked("screen", Process.myUid());
             }
         }
@@ -1997,6 +2027,16 @@
         }
     }
 
+    void keyguardShowingLocked(boolean showing) {
+        if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
+        if (mScreenLocked != showing) {
+            mScreenLocked = showing;
+            if (mScreenOn && !mForceIdle && !mScreenLocked) {
+                becomeActiveLocked("unlocked", Process.myUid());
+            }
+        }
+    }
+
     void scheduleReportActiveLocked(String activeReason, int activeUid) {
         Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
         mHandler.sendMessage(msg);
@@ -3308,6 +3348,7 @@
             pw.print("  mForceIdle="); pw.println(mForceIdle);
             pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mScreenLocked="); pw.println(mScreenLocked);
             pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
             pw.print("  mMotionActive="); pw.println(mMotionListener.active);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9865e35..727e7ee 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -111,6 +111,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.IDeviceIdleController;
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
@@ -286,6 +287,7 @@
     private AlarmManager mAlarmManager;
     private ICompanionDeviceManager mCompanionManager;
     private AccessibilityManager mAccessibilityManager;
+    private IDeviceIdleController mDeviceIdleController;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -659,6 +661,7 @@
 
         @Override
         public void onNotificationClick(int callingUid, int callingPid, String key) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
@@ -683,6 +686,7 @@
         @Override
         public void onNotificationActionClick(int callingUid, int callingPid, String key,
                 int actionIndex) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
@@ -812,6 +816,7 @@
 
         @Override
         public void onNotificationDirectReplied(String key) {
+            exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -1280,6 +1285,8 @@
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
 
         mHandler = new WorkerHandler(looper);
         mRankingThread.start();
@@ -1533,6 +1540,15 @@
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private void exitIdle() {
+        try {
+            if (mDeviceIdleController != null) {
+                mDeviceIdleController.exitIdle("notification interaction");
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
     private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
             boolean fromListener) {
         if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 3475572..aabf9ea 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
 
     <application>
         <uses-library android:name="android.test.runner" />