Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb06e31e authored by Diya Bera's avatar Diya Bera
Browse files

Add lockout and mandatory biometric error constant

Flag: android.hardware.biometrics.flags.mandatory_biometrics
Bug: 358179610
Test: atest PreAuthInfoTest UtilsTest BiometricServiceTest
Change-Id: I12e2364d59f31b6e484e9bcecc72dacb8877675f
parent c23965f5
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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.
+17 −1
Original line number Diff line number Diff line
@@ -79,6 +79,20 @@ public class BiometricManager {
    public static final int BIOMETRIC_ERROR_NO_HARDWARE =
            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,
@@ -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 {}

+11 −21
Original line number Diff line number Diff line
@@ -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.
+10 −1
Original line number Diff line number Diff line
@@ -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:
+33 −2
Original line number Diff line number Diff line
@@ -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);
Loading