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

Commit 09a5df3e authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Improve checkAndGetAuthenticators

1) Device credential and biometrics results are all calculated within
   checkAndGetAuthenticators
2) DevicePolicy restrictions are now appropriately reflected across all
   use cases

Fixes issues where:
  1) When device credential and biometric are both allowed, but none
     are set up. Returns BIOMETRIC_ERROR_NO_BIOMETRICS now
  2) If DevicePolicy prohibits a biometric, we no longer update the
     strength, detected, enrolled fields, which could cause unexpected
     side-effects in the result

Bug: 148398804
Test: atest BiometricServiceTest

Change-Id: Iac561b41574f132fd92fed0f8a5b4baa74f15c01
parent 5c8b722d
Loading
Loading
Loading
Loading
+104 −94
Original line number Original line Diff line number Diff line
@@ -707,7 +707,6 @@ public class BiometricService extends SystemService {
                checkPermission();
                checkPermission();
            }
            }



            if (!Utils.isValidAuthenticatorConfig(authenticators)) {
            if (!Utils.isValidAuthenticatorConfig(authenticators)) {
                throw new SecurityException("Invalid authenticator configuration");
                throw new SecurityException("Invalid authenticator configuration");
            }
            }
@@ -720,19 +719,6 @@ public class BiometricService extends SystemService {
            try {
            try {
                biometricConstantsResult = checkAndGetAuthenticators(userId, bundle, opPackageName,
                biometricConstantsResult = checkAndGetAuthenticators(userId, bundle, opPackageName,
                        false /* checkDevicePolicyManager */).second;
                        false /* checkDevicePolicyManager */).second;
                if (biometricConstantsResult != BiometricConstants.BIOMETRIC_SUCCESS
                        && Utils.isDeviceCredentialAllowed(bundle)) {
                    // If there's an issue with biometrics, but device credential is allowed and
                    // set up, return SUCCESS. If device credential isn't set up either, return
                    // ERROR_NO_DEVICE_CREDENTIAL.
                    if (mTrustManager.isDeviceSecure(userId)) {
                        biometricConstantsResult = BiometricConstants.BIOMETRIC_SUCCESS;
                    } else {
                        biometricConstantsResult =
                                BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL;
                    }
                }

            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception", e);
                Slog.e(TAG, "Remote exception", e);
            } finally {
            } finally {
@@ -1022,9 +1008,8 @@ public class BiometricService extends SystemService {
    }
    }


    /**
    /**
     * Checks if there are any available biometrics, and returns the modality. This method also
     * Depending on the requested authentication (credential/biometric combination), checks their
     * returns errors through the callback (no biometric feature, hardware not detected, no
     * availability.
     * templates enrolled, etc). This service must not start authentication if errors are sent.
     *
     *
     * @param userId the user to check for
     * @param userId the user to check for
     * @param bundle passed from {@link BiometricPrompt}
     * @param bundle passed from {@link BiometricPrompt}
@@ -1037,24 +1022,27 @@ public class BiometricService extends SystemService {
     * {@link BiometricAuthenticator#TYPE_FACE}
     * {@link BiometricAuthenticator#TYPE_FACE}
     * and the error containing one of the {@link BiometricConstants} errors.
     * and the error containing one of the {@link BiometricConstants} errors.
     *
     *
     * TODO(kchyn): Update this to handle DEVICE_CREDENTIAL better, reduce duplicate code in callers
     * TODO(kchyn) should return Pair<Integer, Integer> with `first` being an actual bitfield
     * taking BiometricAuthenticator#TYPE_CREDENTIAL as well.
     *
     */
     */
    private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
    private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
            String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
            String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
        if (!Utils.isBiometricAllowed(bundle)
                && Utils.isDeviceCredentialAllowed(bundle)
                && !mTrustManager.isDeviceSecure(userId)) {
            // If only device credential is being checked, and the user doesn't have one set up
            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
        }


        // Assuming that authenticators are listed in priority-order, the rest of this function
        final boolean biometricRequested = Utils.isBiometricAllowed(bundle);
        // will attempt to find the first authenticator that's as strong or stronger than the
        final boolean credentialRequested = Utils.isDeviceCredentialAllowed(bundle);
        // requested strength, available, enrolled, and enabled. The tricky part is returning the

        // correct error. Error strings that are modality-specific should also respect the
        final boolean biometricOk;
        final boolean credentialOk = mTrustManager.isDeviceSecure(userId);

        // Assuming that biometric authenticators are listed in priority-order, the rest of this
        // function will attempt to find the first authenticator that's as strong or stronger than
        // the requested strength, available, enrolled, and enabled. The tricky part is returning
        // the correct error. Error strings that are modality-specific should also respect the
        // priority-order.
        // priority-order.


        // Find first authenticator that's strong enough, detected, enrolled, and enabled.
        // Find first biometric authenticator that's strong enough, detected, enrolled, and enabled.
        boolean disabledByDevicePolicy = false;
        boolean hasSufficientStrength = false;
        boolean hasSufficientStrength = false;
        boolean isHardwareDetected = false;
        boolean isHardwareDetected = false;
        boolean hasTemplatesEnrolled = false;
        boolean hasTemplatesEnrolled = false;
@@ -1065,52 +1053,96 @@ public class BiometricService extends SystemService {
        for (AuthenticatorWrapper authenticator : mAuthenticators) {
        for (AuthenticatorWrapper authenticator : mAuthenticators) {
            final int actualStrength = authenticator.getActualStrength();
            final int actualStrength = authenticator.getActualStrength();
            final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
            final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
            if (Utils.isAtLeastStrength(actualStrength, requestedStrength)) {

            if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
                disabledByDevicePolicy = true;
                continue;
            }
            disabledByDevicePolicy = false;

            if (!Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
                continue;
            }
            hasSufficientStrength = true;
            hasSufficientStrength = true;
                modality = authenticator.modality;

                if (authenticator.impl.isHardwareDetected(opPackageName)) {
            if (!authenticator.impl.isHardwareDetected(opPackageName)) {
                continue;
            }
            isHardwareDetected = true;
            isHardwareDetected = true;

            if (firstHwAvailable == TYPE_NONE) {
            if (firstHwAvailable == TYPE_NONE) {
                // Store the first one since we want to return the error in correct
                // Store the first one since we want to return the error in correct
                // priority order.
                // priority order.
                        firstHwAvailable = modality;
                firstHwAvailable = authenticator.modality;
            }

            if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
                continue;
            }
            }
                    if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
            hasTemplatesEnrolled = true;
            hasTemplatesEnrolled = true;
                        // If the device policy manager disables a specific biometric, skip it.

                        if (checkDevicePolicyManager &&
            if (!isEnabledForApp(authenticator.modality, userId)) {
                                isBiometricDisabledByDevicePolicy(modality, userId)) {
                continue;
                continue;
            }
            }
                        if (isEnabledForApp(modality, userId)) {
            enabledForApps = true;
            enabledForApps = true;
            modality = authenticator.modality;
            break;
            break;
        }
        }
                    }

                }
        biometricOk = !disabledByDevicePolicy
            }
                && hasSufficientStrength && isHardwareDetected
        }
                && hasTemplatesEnrolled && enabledForApps;


        Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
        Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
                + " checkDevicePolicyManager=" + checkDevicePolicyManager
                + " checkDevicePolicyManager=" + checkDevicePolicyManager
                + " isHardwareDetected=" + isHardwareDetected
                + " isHardwareDetected=" + isHardwareDetected
                + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
                + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
                + " enabledForApps=" + enabledForApps);
                + " enabledForApps=" + enabledForApps

                + " disabledByDevicePolicy=" + disabledByDevicePolicy);
        // Check error conditions

        if (!hasSufficientStrength) {
        if (biometricRequested && credentialRequested) {
            if (credentialOk || biometricOk) {
                if (!biometricOk) {
                    // If there's a problem with biometrics but device credential is
                    // allowed, only show credential UI.
                    bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
                            Authenticators.DEVICE_CREDENTIAL);
                }
                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
            } else {
                return new Pair<>(firstHwAvailable,
                        BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
            }
        } else if (biometricRequested) {
            if (biometricOk) {
                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
            } else if (disabledByDevicePolicy) {
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
            } else if (!hasSufficientStrength) {
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
            } else if (!isHardwareDetected) {
            } else if (!isHardwareDetected) {
            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
                return new Pair<>(firstHwAvailable,
                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
            } else if (!hasTemplatesEnrolled) {
            } else if (!hasTemplatesEnrolled) {
            // Return the modality here so the correct error string can be sent. This error is
                return new Pair<>(firstHwAvailable,
            // preferred over !enabledForApps
                        BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
            return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
            } else if (!enabledForApps) {
            } else if (!enabledForApps) {
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
            } else {
                Slog.e(TAG, "Unexpected case");
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
            }
        } else if (credentialRequested) {
            if (credentialOk) {
                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_SUCCESS);
            } else {
                return new Pair<>(TYPE_NONE,
                        BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
            }
        } else {
            Slog.e(TAG, "No authenticators requested");
            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
        }
        }

        return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
    }
    }


    private boolean isEnabledForApp(int modality, int userId) {
    private boolean isEnabledForApp(int modality, int userId) {
@@ -1549,42 +1581,20 @@ public class BiometricService extends SystemService {
            int callingUid, int callingPid, int callingUserId) {
            int callingUid, int callingPid, int callingUserId) {


        mHandler.post(() -> {
        mHandler.post(() -> {
            int modality = TYPE_NONE;
            int result;

            try {
            try {
                final boolean checkDevicePolicyManager = bundle.getBoolean(
                final boolean checkDevicePolicyManager = bundle.getBoolean(
                        BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
                        BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
                final Pair<Integer, Integer> pair = checkAndGetAuthenticators(userId, bundle,
                final Pair<Integer, Integer> pair = checkAndGetAuthenticators(userId, bundle,
                        opPackageName, checkDevicePolicyManager);
                        opPackageName, checkDevicePolicyManager);
                modality = pair.first;
                final int modality = pair.first;
                result = pair.second;
                final int result = pair.second;
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception", e);
                result = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
            }


            try {
                if (result == BiometricConstants.BIOMETRIC_SUCCESS) {
                if (result == BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL) {
                    // If the app allowed device credential but the user hasn't set it up yet,
                    // return this error.
                    receiver.onError(modality, result, 0 /* vendorCode */);
                } else if (result != BiometricConstants.BIOMETRIC_SUCCESS) {
                    if (Utils.isDeviceCredentialAllowed(bundle)) {
                        // If there's a problem with biometrics but device credential is allowed,
                        // only show credential UI.
                        bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
                                Authenticators.DEVICE_CREDENTIAL);
                    authenticateInternal(token, sessionId, userId, receiver, opPackageName,
                    authenticateInternal(token, sessionId, userId, receiver, opPackageName,
                            bundle, callingUid, callingPid, callingUserId, modality);
                            bundle, callingUid, callingPid, callingUserId, modality);
                } else {
                } else {
                    receiver.onError(modality, result, 0 /* vendorCode */);
                    receiver.onError(modality, result, 0 /* vendorCode */);
                }
                }
                } else {
                    // BIOMETRIC_SUCCESS, proceed to authentication
                    authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
                            callingUid, callingPid, callingUserId, modality);
                }
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception", e);
                Slog.e(TAG, "Remote exception", e);
            }
            }
+40 −22
Original line number Original line Diff line number Diff line
@@ -421,6 +421,7 @@ public class BiometricServiceTest {
    public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
    public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
        when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
        when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
                true /* requireConfirmation */,
                true /* requireConfirmation */,
                Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
                Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
@@ -466,6 +467,22 @@ public class BiometricServiceTest {
                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
    }
    }


    @Test
    public void testAuthenticate_no_Biometrics_noCredential() throws Exception {
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
        when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);

        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
                true /* requireConfirmation */,
                Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG);
        waitForIdle();

        verify(mReceiver1).onError(anyInt() /* modality */,
                eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS)/* error */,
                eq(0) /* vendorCode */);
    }

    @Test
    @Test
    public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
    public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
            throws Exception {
            throws Exception {
@@ -1216,7 +1233,8 @@ public class BiometricServiceTest {
        when(mDevicePolicyManager
        when(mDevicePolicyManager
                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                .thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
                .thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
                Authenticators.BIOMETRIC_STRONG);
        waitForIdle();
        waitForIdle();
        assertEquals(mBiometricService.mPendingAuthSession.mState,
        assertEquals(mBiometricService.mPendingAuthSession.mState,
                BiometricService.STATE_AUTH_CALLED);
                BiometricService.STATE_AUTH_CALLED);
@@ -1233,7 +1251,8 @@ public class BiometricServiceTest {
        when(mDevicePolicyManager
        when(mDevicePolicyManager
                .getKeyguardDisabledFeatures(any() /* admin*/, anyInt() /* userHandle */))
                .getKeyguardDisabledFeatures(any() /* admin*/, anyInt() /* userHandle */))
                .thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FACE);
                .thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FACE);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
                Authenticators.BIOMETRIC_STRONG);
        waitForIdle();
        waitForIdle();
        assertEquals(mBiometricService.mPendingAuthSession.mState,
        assertEquals(mBiometricService.mPendingAuthSession.mState,
                BiometricService.STATE_AUTH_CALLED);
                BiometricService.STATE_AUTH_CALLED);
@@ -1247,27 +1266,24 @@ public class BiometricServiceTest {
    public void testWorkAuthentication_fingerprintFailsIfDisabledByDevicePolicyManager()
    public void testWorkAuthentication_fingerprintFailsIfDisabledByDevicePolicyManager()
            throws Exception {
            throws Exception {
        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
        when(mDevicePolicyManager
        when(mDevicePolicyManager
                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1);

        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
                Authenticators.BIOMETRIC_STRONG);
        waitForIdle();
        waitForIdle();
        assertNotNull(mBiometricService.mCurrentAuthSession);
        verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_NONE),
        assertEquals(mBiometricService.mCurrentAuthSession.mState,
                eq(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0) /* vendorCode */);
                BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL);
    }


    @Test
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver2,
    public void testWorkAuthentication_faceFailsIfDisabledByDevicePolicyManager() throws Exception {
                Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
        when(mDevicePolicyManager
                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
        invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1);
        waitForIdle();
        waitForIdle();
        assertNotNull(mBiometricService.mCurrentAuthSession);
        assertNotNull(mBiometricService.mCurrentAuthSession);
        assertEquals(mBiometricService.mCurrentAuthSession.mState,
        assertEquals(mBiometricService.mCurrentAuthSession.mState,
                BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL);
                BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL);
        verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt());
    }
    }


    // Helper methods
    // Helper methods
@@ -1376,33 +1392,35 @@ public class BiometricServiceTest {
                0 /* userId */,
                0 /* userId */,
                receiver,
                receiver,
                TEST_PACKAGE_NAME /* packageName */,
                TEST_PACKAGE_NAME /* packageName */,
                createTestBiometricPromptBundle(requireConfirmation, authenticators));
                createTestBiometricPromptBundle(requireConfirmation, authenticators,
                        false /* checkDevicePolicy */));
    }
    }


    private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service,
    private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service,
            IBiometricServiceReceiver receiver) throws Exception {
            IBiometricServiceReceiver receiver, Integer authenticators) throws Exception {
        final Bundle bundle = new Bundle();
        bundle.putBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, true);
        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
        bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
        service.authenticate(
        service.authenticate(
                new Binder() /* token */,
                new Binder() /* token */,
                0 /* sessionId */,
                0 /* sessionId */,
                0 /* userId */,
                0 /* userId */,
                receiver,
                receiver,
                TEST_PACKAGE_NAME /* packageName */,
                TEST_PACKAGE_NAME /* packageName */,
                bundle);
                createTestBiometricPromptBundle(false /* requireConfirmation */, authenticators,
                        true /* checkDevicePolicy */));
    }
    }


    private static Bundle createTestBiometricPromptBundle(
    private static Bundle createTestBiometricPromptBundle(
            boolean requireConfirmation,
            boolean requireConfirmation,
            Integer authenticators) {
            Integer authenticators,
            boolean checkDevicePolicy) {
        final Bundle bundle = new Bundle();
        final Bundle bundle = new Bundle();
        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);


        if (authenticators != null) {
        if (authenticators != null) {
            bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
            bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
        }
        }
        if (checkDevicePolicy) {
            bundle.putBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, true);
        }
        return bundle;
        return bundle;
    }
    }