diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 44e3d1ede19c2..e6f25cb88006a 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -267,17 +267,30 @@ class PreAuthInfo { } private Pair calculateErrorByPriority() { - // If the caller requested STRONG, and the device contains both STRONG and non-STRONG - // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's - // BIOMETRIC_INSUFFICIENT_STRENGTH error. Pretty sure we can always prioritize - // BIOMETRIC_NOT_ENROLLED over any other error (unless of course its calculation is - // wrong, in which case we should fix that instead). + Pair sensorNotEnrolled = null; + Pair sensorLockout = null; for (Pair pair : ineligibleSensors) { + int status = pair.second; + if (status == BIOMETRIC_LOCKOUT_TIMED || status == BIOMETRIC_LOCKOUT_PERMANENT) { + sensorLockout = pair; + } if (pair.second == BIOMETRIC_NOT_ENROLLED) { - return pair; + sensorNotEnrolled = pair; } } + // If there is a sensor locked out, prioritize lockout over other sensor's error. + // See b/286923477. + if (sensorLockout != null) { + return sensorLockout; + } + + // If the caller requested STRONG, and the device contains both STRONG and non-STRONG + // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's + // BIOMETRIC_INSUFFICIENT_STRENGTH error. + if (sensorNotEnrolled != null) { + return sensorNotEnrolled; + } return ineligibleSensors.get(0); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 67be37616d5f1..8091273a80385 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -18,7 +18,6 @@ package com.android.server.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.Authenticators; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI; @@ -902,6 +901,45 @@ public class BiometricServiceTest { eq(biometricPromptError), eq(0) /* vendorCode */); } + @Test + public void testMultiBiometricAuth_whenLockoutTimed_sendsErrorAndModality() + throws Exception { + testMultiBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_TIMED, + BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT); + } + + @Test + public void testMultiBiometricAuth_whenLockoutPermanent_sendsErrorAndModality() + throws Exception { + testMultiBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_PERMANENT, + BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT); + } + + private void testMultiBiometricAuth_whenLockout(@LockoutTracker.LockoutMode int lockoutMode, + int biometricPromptError) throws Exception { + final int[] modalities = new int[] { + TYPE_FINGERPRINT, + BiometricAuthenticator.TYPE_FACE, + }; + + final int[] strengths = new int[] { + Authenticators.BIOMETRIC_STRONG, + Authenticators.BIOMETRIC_STRONG, + }; + setupAuthForMultiple(modalities, strengths); + + when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt())) + .thenReturn(lockoutMode); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, null /* authenticators */); + waitForIdle(); + + // The lockout error should be sent, instead of ERROR_NONE_ENROLLED. See b/286923477. + verify(mReceiver1).onError(eq(TYPE_FINGERPRINT), + eq(biometricPromptError), eq(0) /* vendorCode */); + } + @Test public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential() throws Exception {