From ea1d5ca74f98fa053490a18dc3b9fb33e9a24557 Mon Sep 17 00:00:00 2001 From: Hao Dong Date: Mon, 12 Jun 2023 22:25:19 +0000 Subject: [PATCH] Prioritize lockout error over not enrolled error. Bug: 286923477 Test: atest BiometricServiceTest Test: manual test - 1. enroll only one biometric 2. try enough failed attempts to lock it out on biometric prompt 3. try to launch biometric prompt and the error message should be lockout error Change-Id: Ie14909ba9beca8f946d5abde7d690693529a8917 --- .../server/biometrics/PreAuthInfo.java | 25 +++++++++--- .../biometrics/BiometricServiceTest.java | 40 ++++++++++++++++++- 2 files changed, 58 insertions(+), 7 deletions(-) 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 {