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

Commit deea016d authored by Arpan's avatar Arpan Committed by Arpan Kaphle
Browse files

General Fallback for BiometricPrompt

Previously, we nuanced the fallback around the DEVICE_CREDENTIALS bit,
and allowed other cases to propagate.

Now, we generalize the scope of the initial fallback to avoid having to
call the biometric API at all (and thus avoid the onError() response for
a vast majority of the use cases).

Bug: 334197980
Test: Video tests in the bug and build test
Change-Id: Iee78237156c15400ef5a293ad86b38a43b128e9e
parent 9666bcc7
Loading
Loading
Loading
Loading
+18 −18
Original line number Diff line number Diff line
@@ -210,7 +210,7 @@ private fun runBiometricFlow(
    onCancelFlowAndFinish: () -> Unit
) {
    try {
        if (onlyUsingDeviceCredentials(biometricDisplayInfo, context)) {
        if (!canCallBiometricPrompt(biometricDisplayInfo, context)) {
            onBiometricFailureFallback(biometricFlowType)
            return
        }
@@ -251,40 +251,40 @@ private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
 * consistency because for biometrics to exist, **device credentials must exist**. Thus, fallbacks
 * occur if *only* device credentials are available, to avoid going right into the PIN screen.
 * Note that if device credential is the only available modality but not requested, or if none
 * of the requested modalities are available, we propagate the error to the provider instead of
 * falling back and expect them to handle it as they would prior.
 * // TODO(b/334197980) : Finalize error propagation/not propagation in real use cases
 * of the requested modalities are available, we fallback to the normal flow to ensure a selector
 * shows up.
 * // TODO(b/334197980) : While we already fallback in cases the selector doesn't show, confirm
 * // final plan.
 */
private fun onlyUsingDeviceCredentials(
private fun canCallBiometricPrompt(
    biometricDisplayInfo: BiometricDisplayInfo,
    context: Context
): Boolean {
    val allowedAuthenticators = biometricDisplayInfo.biometricRequestInfo.allowedAuthenticators
    if (allowedAuthenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
        return true
        return false
    }

    val allowedAuthContainsDeviceCredential = containsBiometricAuthenticatorWithDeviceCredentials(
        allowedAuthenticators)
    val biometricManager = context.getSystemService(Context.BIOMETRIC_SERVICE) as BiometricManager

    if (!allowedAuthContainsDeviceCredential) {
        // At this point, allowed authenticators is requesting biometrics without device creds.
        // Thus, a fallback mechanism will be displayed via our own negative button - "cancel".
        // Beyond this point, fallbacks will occur if none of the stronger authenticators can
        // be used.
    if (biometricManager.canAuthenticate(allowedAuthenticators) !=
        BiometricManager.BIOMETRIC_SUCCESS) {
        return false
    }

    val biometricManager = context.getSystemService(Context.BIOMETRIC_SERVICE) as BiometricManager
    if (ifOnlySupportsAtMostDeviceCredentials(biometricManager)) return false

    if (allowedAuthContainsDeviceCredential &&
        biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) !=
    return true
}

private fun ifOnlySupportsAtMostDeviceCredentials(biometricManager: BiometricManager): Boolean {
    if (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) !=
        BiometricManager.BIOMETRIC_SUCCESS &&
        biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) !=
        BiometricManager.BIOMETRIC_SUCCESS) {
        BiometricManager.BIOMETRIC_SUCCESS
    ) {
        return true
    }

    return false
}