summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/biometrics/BiometricConstants.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java18
-rw-r--r--services/core/java/com/android/server/biometrics/PreAuthInfo.java32
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java40
7 files changed, 136 insertions, 27 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 61d87026b6e9..8975191b54c1 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -162,6 +162,13 @@ public interface BiometricConstants {
* @hide
*/
int BIOMETRIC_ERROR_POWER_PRESSED = 19;
+
+ /**
+ * Mandatory biometrics is not in effect.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE = 20;
+
/**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index de1cac47ff46..9bc46b9f382a 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -80,6 +80,20 @@ public class BiometricManager {
BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
/**
+ * Lockout error.
+ * @hide
+ */
+ public static final int BIOMETRIC_ERROR_LOCKOUT =
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+
+ /**
+ * Mandatory biometrics is not effective.
+ * @hide
+ */
+ public static final int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE =
+ BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+
+ /**
* A security vulnerability has been discovered and the sensor is unavailable until a
* security update has addressed this issue. This error can be received if for example,
* authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
@@ -113,7 +127,9 @@ public class BiometricManager {
BIOMETRIC_ERROR_HW_UNAVAILABLE,
BIOMETRIC_ERROR_NONE_ENROLLED,
BIOMETRIC_ERROR_NO_HARDWARE,
- BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
+ BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
+ BIOMETRIC_ERROR_LOCKOUT,
+ BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE})
@Retention(RetentionPolicy.SOURCE)
public @interface BiometricError {}
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 0bd22f3da67f..f0da67bd9f9a 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -77,13 +77,15 @@ class PreAuthInfo {
private final int mBiometricStrengthRequested;
private final BiometricCameraManager mBiometricCameraManager;
private final boolean mOnlyMandatoryBiometricsRequested;
+ private final boolean mIsMandatoryBiometricsAuthentication;
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
PromptInfo promptInfo, int userId, Context context,
BiometricCameraManager biometricCameraManager,
- boolean isOnlyMandatoryBiometricsRequested) {
+ boolean isOnlyMandatoryBiometricsRequested,
+ boolean isMandatoryBiometricsAuthentication) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
mBiometricCameraManager = biometricCameraManager;
@@ -97,6 +99,7 @@ class PreAuthInfo {
this.userId = userId;
this.context = context;
this.mOnlyMandatoryBiometricsRequested = isOnlyMandatoryBiometricsRequested;
+ this.mIsMandatoryBiometricsAuthentication = isMandatoryBiometricsAuthentication;
}
static PreAuthInfo create(ITrustManager trustManager,
@@ -110,10 +113,12 @@ class PreAuthInfo {
final boolean isOnlyMandatoryBiometricsRequested = promptInfo.getAuthenticators()
== BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+ boolean isMandatoryBiometricsAuthentication = false;
if (dropCredentialFallback(promptInfo.getAuthenticators(),
settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
userId), trustManager)) {
+ isMandatoryBiometricsAuthentication = true;
promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
}
@@ -166,7 +171,8 @@ class PreAuthInfo {
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
eligibleSensors, ineligibleSensors, credentialAvailable, promptInfo, userId,
- context, biometricCameraManager, isOnlyMandatoryBiometricsRequested);
+ context, biometricCameraManager, isOnlyMandatoryBiometricsRequested,
+ isMandatoryBiometricsAuthentication);
}
private static boolean dropCredentialFallback(int authenticators,
@@ -387,25 +393,6 @@ class PreAuthInfo {
status = CREDENTIAL_NOT_ENROLLED;
}
}
- } else if (Flags.mandatoryBiometrics() && mOnlyMandatoryBiometricsRequested) {
- if (!eligibleSensors.isEmpty()) {
- for (BiometricSensor sensor : eligibleSensors) {
- modality |= sensor.modality;
- }
-
- if (modality == TYPE_FACE && cameraPrivacyEnabled) {
- // If the only modality requested is face, credential is unavailable,
- // and the face sensor privacy is enabled then return
- // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
- //
- // Note: This sensor will not be eligible for calls to authenticate.
- status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
- } else {
- status = AUTHENTICATOR_OK;
- }
- } else {
- status = MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
- }
} else if (mBiometricRequested) {
if (!eligibleSensors.isEmpty()) {
for (BiometricSensor sensor : eligibleSensors) {
@@ -434,6 +421,9 @@ class PreAuthInfo {
} else if (credentialRequested) {
modality |= TYPE_CREDENTIAL;
status = credentialAvailable ? AUTHENTICATOR_OK : CREDENTIAL_NOT_ENROLLED;
+ } else if (Flags.mandatoryBiometrics() && mOnlyMandatoryBiometricsRequested
+ && !mIsMandatoryBiometricsAuthentication) {
+ status = MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
} else {
// This should not be possible via the public API surface and is here mainly for
// "correctness". An exception should have been thrown before getting here.
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index df29ca45930e..3e4e7dd771d2 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -35,6 +35,7 @@ import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_SENSOR_PRIVACY_ENABLED;
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
+import static com.android.server.biometrics.PreAuthInfo.MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,6 +49,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
@@ -309,11 +311,16 @@ public class Utils {
break;
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
- biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
+ biometricManagerCode = Flags.mandatoryBiometrics()
+ ? BiometricManager.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricManager.BIOMETRIC_SUCCESS;
break;
case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
break;
+ case BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+ break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -375,6 +382,8 @@ public class Utils {
return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
+ case MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR:
+ return BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
case BIOMETRIC_HARDWARE_NOT_DETECTED:
case BIOMETRIC_NOT_ENABLED_FOR_APPS:
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 a4222ff5650b..d2961bc8a90f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -79,6 +79,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -1488,15 +1489,30 @@ public class BiometricServiceTest {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testCanAuthenticate_whenLockoutTimed() throws Exception {
testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_TIMED);
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testCanAuthenticate_whenLockoutPermanent() throws Exception {
testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_PERMANENT);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenLockoutTimed_returnsLockoutError() throws Exception {
+ testCanAuthenticate_whenLockedOut_returnLockoutError(LockoutTracker.LOCKOUT_TIMED);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenLockoutPermanent_returnsLockoutError() throws Exception {
+ testCanAuthenticate_whenLockedOut_returnLockoutError(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
private void testCanAuthenticate_whenLockedOut(@LockoutTracker.LockoutMode int lockoutMode)
throws Exception {
// When only biometric is requested, and sensor is strong enough
@@ -1510,6 +1526,21 @@ public class BiometricServiceTest {
invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
}
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ private void testCanAuthenticate_whenLockedOut_returnLockoutError(
+ @LockoutTracker.LockoutMode int lockoutMode)
+ throws Exception {
+ // When only biometric is requested, and sensor is strong enough
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+ when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(lockoutMode);
+
+ // Lockout is not considered an error for BiometricManager#canAuthenticate
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_LOCKOUT,
+ invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
+ }
+
@Test
@RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testCanAuthenticate_whenMandatoryBiometricsRequested()
@@ -1529,7 +1560,7 @@ public class BiometricServiceTest {
when(mTrustManager.isInSignificantPlace()).thenReturn(true);
- assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
}
@@ -1572,7 +1603,7 @@ public class BiometricServiceTest {
setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
- assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
when(mTrustManager.isInSignificantPlace()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index 240da9fe46bd..4c3a233fdd97 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -231,7 +231,7 @@ public class PreAuthInfoTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
- public void testMandatoryBiometricsStatus_whenRequirementsNotSatisfiedAndSensorAvailable()
+ public void testMandatoryBiometricsAndStrongBiometricsStatus_whenRequirementsNotSatisfied()
throws Exception {
when(mTrustManager.isInSignificantPlace()).thenReturn(true);
@@ -246,6 +246,24 @@ public class PreAuthInfoTest {
assertThat(preAuthInfo.eligibleSensors).hasSize(1);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testMandatoryBiometricsStatus_whenRequirementsNotSatisfiedAndSensorAvailable()
+ throws Exception {
+ when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+ final BiometricSensor sensor = getFaceSensor();
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
+ BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+ assertThat(preAuthInfo.eligibleSensors).hasSize(0);
+ }
+
private BiometricSensor getFingerprintSensor() {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index cb75e1ab6cce..14cb22d7698e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -26,16 +26,25 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
@Presubmit
@SmallTest
public class UtilsTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Test
public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
@@ -215,7 +224,8 @@ public class UtilsTest {
}
@Test
- public void testBiometricConstantsConversion() {
+ @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testBiometricConstantsConversionLegacy() {
final int[][] testCases = {
{BiometricConstants.BIOMETRIC_SUCCESS,
BiometricManager.BIOMETRIC_SUCCESS},
@@ -240,6 +250,34 @@ public class UtilsTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testBiometricConstantsConversion() {
+ final int[][] testCases = {
+ {BiometricConstants.BIOMETRIC_SUCCESS,
+ BiometricManager.BIOMETRIC_SUCCESS},
+ {BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+ {BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+ {BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE},
+ {BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE},
+ {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
+ {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+ BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
+ {BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
+ BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE}
+ };
+
+ for (int i = 0; i < testCases.length; i++) {
+ assertEquals(testCases[i][1],
+ Utils.biometricConstantsToBiometricManager(testCases[i][0]));
+ }
+ }
+
+ @Test
public void testGetAuthenticationTypeForResult_getsCorrectType() {
assertEquals(Utils.getAuthenticationTypeForResult(
BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),