Merge "Change string resource for face not detected error" into qt-r1-dev
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index c8f0e09..12b285a 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -262,7 +262,7 @@
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void enroll(byte[] token, CancellationSignal cancel,
+    public void enroll(int userId, byte[] token, CancellationSignal cancel,
             EnrollmentCallback callback, int[] disabledFeatures) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -281,7 +281,7 @@
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enroll");
-                mService.enroll(mToken, token, mServiceReceiver,
+                mService.enroll(userId, mToken, token, mServiceReceiver,
                         mContext.getOpPackageName(), disabledFeatures);
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception in enroll: ", e);
@@ -339,12 +339,13 @@
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void setFeature(int feature, boolean enabled, byte[] token,
+    public void setFeature(int userId, int feature, boolean enabled, byte[] token,
             SetFeatureCallback callback) {
         if (mService != null) {
             try {
                 mSetFeatureCallback = callback;
-                mService.setFeature(feature, enabled, token, mServiceReceiver);
+                mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
+                        mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -355,11 +356,11 @@
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void getFeature(int feature, GetFeatureCallback callback) {
+    public void getFeature(int userId, int feature, GetFeatureCallback callback) {
         if (mService != null) {
             try {
                 mGetFeatureCallback = callback;
-                mService.getFeature(feature, mServiceReceiver);
+                mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -414,7 +415,8 @@
             try {
                 mRemovalCallback = callback;
                 mRemovalFace = face;
-                mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver);
+                mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
+                        mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception in remove: ", e);
                 if (callback != null) {
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 601be75..b6a0afb 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -50,14 +50,15 @@
             int callingUid, int callingPid, int callingUserId, boolean fromClient);
 
     // Start face enrollment
-    void enroll(IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+    void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
 
     // Any errors resulting from this call will be returned to the listener
-    void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver);
+    void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
+            String opPackageName);
 
     // Rename the face specified by faceId to the given name
     void rename(int faceId, String name);
@@ -98,10 +99,10 @@
     // Enumerate all faces
     void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
 
-    void setFeature(int feature, boolean enabled, in byte [] token,
-            IFaceServiceReceiver receiver);
+    void setFeature(int userId, int feature, boolean enabled, in byte [] token,
+            IFaceServiceReceiver receiver, String opPackageName);
 
-    void getFeature(int feature, IFaceServiceReceiver receiver);
+    void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName);
 
     void userActivity();
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0a834c8..05e14a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -30,6 +30,12 @@
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+
 import android.annotation.AnyThread;
 import android.annotation.MainThread;
 import android.app.ActivityManager;
@@ -672,8 +678,7 @@
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
-            mLockPatternUtils.requireStrongAuth(
-                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+            mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
                     getCurrentUser());
         }
 
@@ -833,8 +838,7 @@
         }
 
         if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
-            mLockPatternUtils.requireStrongAuth(
-                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+            mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
                     getCurrentUser());
         }
 
@@ -953,8 +957,8 @@
     }
 
     public boolean isUserInLockdown(int userId) {
-        return mStrongAuthTracker.getStrongAuthForUser(userId)
-                == LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+        return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId),
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
     }
 
     public boolean userNeedsStrongAuth() {
@@ -962,6 +966,10 @@
                 != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
     }
 
+    private boolean containsFlag(int haystack, int needle) {
+        return (haystack & needle) != 0;
+    }
+
     public boolean needsSlowUnlockTransition() {
         return mNeedsSlowUnlockTransition;
     }
@@ -1680,8 +1688,12 @@
         final int user = getCurrentUser();
         final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
         final boolean isLockOutOrLockDown =
-                strongAuth == StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
-                        || strongAuth == StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_LOCKOUT)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+        final boolean isEncryptedOrTimedOut =
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
 
         boolean canBypass = mKeyguardBypassController != null
                 && mKeyguardBypassController.canBypass();
@@ -1690,13 +1702,18 @@
         // TrustAgents or biometrics are keeping the device unlocked.
         boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
 
+        // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
+        // Lockout/lockdown modes shouldn't scan, since they are more explicit.
+        boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass && !mBouncer)
+                && !isLockOutOrLockDown;
+
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant())
                 && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
                 && !mKeyguardGoingAway && mFaceSettingEnabledForUser && !mLockIconPressed
-                && mUserManager.isUserUnlocked(user) && mIsPrimaryUser
-                && !mSecureCameraLaunched && !isLockOutOrLockDown;
+                && strongAuthAllowsScanning && mIsPrimaryUser
+                && !mSecureCameraLaunched;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index fdf18e0..e6a88b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -410,7 +410,7 @@
             } else if (unlockingAllowed) {
                 return faceStayingOnKeyguard ? MODE_ONLY_WAKE : MODE_UNLOCK;
             } else if (face) {
-                return MODE_NONE;
+                return faceStayingOnKeyguard ? MODE_NONE : MODE_SHOW_BOUNCER;
             } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 return MODE_SHOW_BOUNCER;
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2e02fd5..bdc6341 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -317,13 +318,44 @@
 
     @Test
     public void skipsAuthentication_whenEncryptedKeyguard() {
-        reset(mUserManager);
-        when(mUserManager.isUserUnlocked(anyInt())).thenReturn(false);
+        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
+                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
 
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+    }
+
+    @Test
+    public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
+        testStrongAuthExceptOnBouncer(
+                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+    }
+
+    @Test
+    public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
+        testStrongAuthExceptOnBouncer(
+                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+    }
+
+    private void testStrongAuthExceptOnBouncer(int strongAuth) {
+        when(mKeyguardBypassController.canBypass()).thenReturn(true);
+        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
+
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+
+        // Stop scanning when bouncer becomes visible
+        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true /* showingBouncer */);
+        mTestableLooper.processAllMessages();
+        clearInvocations(mFaceManager);
+        mKeyguardUpdateMonitor.requestFaceAuth();
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
     }
 
     @Test
@@ -357,6 +389,28 @@
     }
 
     @Test
+    public void testIgnoresAuth_whenLockdown() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
+                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+    }
+
+    @Test
+    public void testIgnoresAuth_whenLockout() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
+                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
+
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+    }
+
+    @Test
     public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
         mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 4e86f19..748f509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -155,6 +155,30 @@
     }
 
     @Test
+    public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showBouncer() {
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FACE);
+
+        verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() {
+        mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FACE);
+
+        verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+    }
+
+    @Test
     public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
         when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index def7f75..72bac74 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -346,10 +346,11 @@
         }
 
         @Override // Binder call
-        public void enroll(final IBinder token, final byte[] cryptoToken,
+        public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures) {
             checkPermission(MANAGE_BIOMETRIC);
+            updateActiveGroup(userId, opPackageName);
 
             mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
                     UserHandle.CURRENT);
@@ -448,8 +449,9 @@
 
         @Override // Binder call
         public void remove(final IBinder token, final int faceId, final int userId,
-                final IFaceServiceReceiver receiver) {
+                final IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
+            updateActiveGroup(userId, opPackageName);
 
             if (token == null) {
                 Slog.w(TAG, "remove(): token is null");
@@ -612,9 +614,10 @@
         }
 
         @Override
-        public void setFeature(int feature, boolean enabled, final byte[] token,
-                IFaceServiceReceiver receiver) {
+        public void setFeature(int userId, int feature, boolean enabled, final byte[] token,
+                IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
+            updateActiveGroup(userId, opPackageName);
 
             mHandler.post(() -> {
                 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
@@ -644,8 +647,10 @@
         }
 
         @Override
-        public void getFeature(int feature, IFaceServiceReceiver receiver) {
+        public void getFeature(int userId, int feature, IFaceServiceReceiver receiver,
+                final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
+            updateActiveGroup(userId, opPackageName);
 
             mHandler.post(() -> {
                 // This should ideally return tri-state, but the user isn't shown settings unless