Disallow entering PiP without dismissing keyguard.

- If an activity is showing on the keyguard and enters picture-
  in-picture, then prompt the user to authenticate first
- Fixing NPE in SystemUI due to null runnable being added to the
  post-keyguard-gone callbacks
- Prevent FLAG_SHOW_WHEN_LOCKED from applying when determining
  visibility over keyguard for activities that are in the pinned
  stack

Bug: 33660880
Test: android.server.cts.KeyguardLockedTests
Test: #testEnterPipOverKeyguard

Change-Id: I89477a8a0067e285e5d0122e918fac45274c57ad
(cherry picked from commit 0655da0374caff1569c09017e17ac28c4b0b7069)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ea4e102..34a0ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3592,17 +3592,14 @@
             final boolean dismissShade,
             final boolean afterKeyguardGone,
             final boolean deferred) {
-        final Runnable dismissAction = () -> {
-            if (runnable != null) {
-                AsyncTask.execute(runnable);
-            }
-        };
         dismissKeyguardThenExecute(() -> {
-            if (mStatusBarKeyguardViewManager.isShowing()
-                    && mStatusBarKeyguardViewManager.isOccluded()) {
-                mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
-            } else {
-                dismissAction.run();
+            if (runnable != null) {
+                if (mStatusBarKeyguardViewManager.isShowing()
+                        && mStatusBarKeyguardViewManager.isOccluded()) {
+                    mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+                } else {
+                    AsyncTask.execute(runnable);
+                }
             }
             if (dismissShade) {
                 if (mExpandedVisible) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1583023..87b4221 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6710,6 +6710,13 @@
         }
     }
 
+    /**
+     * @return whther the keyguard is currently locked.
+     */
+    boolean isKeyguardLocked() {
+        return mKeyguardController.isKeyguardLocked();
+    }
+
     final void finishBooting() {
         synchronized (this) {
             if (!mBootAnimationComplete) {
@@ -7557,10 +7564,40 @@
                 final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
                         "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
                         true /* checkActivityVisibility */);
+                final Runnable enterPipRunnable = () -> {
+                    r.pictureInPictureArgs.aspectRatio = aspectRatio;
+                    enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
+                            true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+                };
 
-                r.pictureInPictureArgs.aspectRatio = aspectRatio;
-                enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
-                        true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+                if (isKeyguardLocked()) {
+                    // If the keyguard is showing or occluded, then try and dismiss it before
+                    // entering picture-in-picture (this will prompt the user to authenticate if the
+                    // device is currently locked).
+                    try {
+                        dismissKeyguard(token, new IKeyguardDismissCallback.Stub() {
+                            @Override
+                            public void onDismissError() throws RemoteException {
+                                // Do nothing
+                            }
+
+                            @Override
+                            public void onDismissSucceeded() throws RemoteException {
+                                mHandler.post(enterPipRunnable);
+                            }
+
+                            @Override
+                            public void onDismissCancelled() throws RemoteException {
+                                // Do nothing
+                            }
+                        });
+                    } catch (RemoteException e) {
+                        // Local call
+                    }
+                } else {
+                    // Enter picture in picture immediately otherwise
+                    enterPipRunnable.run();
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ba95abd..beeadac 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1810,9 +1810,10 @@
      */
     private boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
             boolean isTop) {
+        final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
         final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing();
         final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
-        final boolean showWhenLocked = r.hasShowWhenLockedWindows();
+        final boolean showWhenLocked = r.hasShowWhenLockedWindows() && !isInPinnedStack;
         final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
         if (shouldBeVisible) {
             if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
@@ -1885,10 +1886,13 @@
     private boolean enterPictureInPictureOnActivityInvisible(ActivityRecord r) {
         final boolean hasPinnedStack =
                 mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+        final boolean isKeyguardLocked = mService.isKeyguardLocked();
         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, " enterPictureInPictureOnInvisible="
                 + r.shouldEnterPictureInPictureOnInvisible()
-                + " hasPinnedStack=" + hasPinnedStack);
-        if (!hasPinnedStack && r.visible && r.shouldEnterPictureInPictureOnInvisible()) {
+                + " hasPinnedStack=" + hasPinnedStack
+                + " isKeyguardLocked=" + isKeyguardLocked);
+        if (!hasPinnedStack && !isKeyguardLocked && r.visible &&
+                r.shouldEnterPictureInPictureOnInvisible()) {
             r.setEnterPipOnMoveToBackground(false);
 
             // Enter picture in picture, but don't move the home stack to the front