Loading packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +1 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,7 @@ class CredentialSelectorActivity : ComponentActivity() { return Triple(true, false, null) } val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationExplanation() viewModel?.onDeveloperCancellationReceivedForBiometricPrompt() Log.d( Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" + " ui = $shouldShowCancellationUi") Loading packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +46 −7 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.credentialmanager import android.app.Activity import android.hardware.biometrics.BiometricPrompt import android.hardware.biometrics.BiometricPrompt.AuthenticationResult import android.os.CancellationSignal import android.os.IBinder import android.text.TextUtils import android.util.Log Loading Loading @@ -232,8 +233,13 @@ class CredentialSelectorViewModel( authResult: BiometricPrompt.AuthenticationResult? = null, authError: BiometricError? = null, ) { if (authError == null) { Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" + ", key=${entry.entryKey}, subkey=${entry.entrySubkey}}") } else { Log.d(Constants.LOG_TAG, "Biometric flow error: ${authError.errorCode} " + "propagating to provider, message: ${authError.errorMessage}.") } uiState = if (entry.pendingIntent != null) { uiState.copy( selectedEntry = entry, Loading Loading @@ -385,9 +391,15 @@ class CredentialSelectorViewModel( val providerId = selectedEntry.providerId val entryKey = selectedEntry.entryKey val entrySubkey = selectedEntry.entrySubkey if (authError == null) { Log.d( Constants.LOG_TAG, "Option selected for entry: " + " {provider=$providerId, key=$entryKey, subkey=$entrySubkey") " {provider=$providerId, key=$entryKey, subkey=$entrySubkey" ) } else { Log.d(Constants.LOG_TAG, "Biometric flow error: ${authError.errorCode} " + "propagating to provider, message: ${authError.errorMessage}.") } if (selectedEntry.pendingIntent != null) { uiState = uiState.copy( selectedEntry = selectedEntry, Loading Loading @@ -423,6 +435,33 @@ class CredentialSelectorViewModel( /***** Biometric Flow Callbacks *****/ /**************************************************************************/ /** * Cancels the biometric prompt's cancellation signal. Should only be called when the credential * manager ui receives a developer cancellation signal. If the prompt is already done, we do * not allow a cancellation, given the UI cancellation will be caught by the backend. We also * set the biometricStatus to CANCELED, so that only in this case, we do *not* propagate the * ERROR_CANCELED when a developer cancellation signal is the root cause. */ fun onDeveloperCancellationReceivedForBiometricPrompt() { val biometricCancellationSignal = uiState.biometricState.biometricCancellationSignal if (!biometricCancellationSignal.isCanceled && uiState.biometricState.biometricStatus != BiometricPromptState.COMPLETE) { uiState = uiState.copy( biometricState = uiState.biometricState.copy( biometricStatus = BiometricPromptState.CANCELED ) ) biometricCancellationSignal.cancel() } } /** * Retrieve the biometric prompt's cancellation signal (e.g. to pass into the 'authenticate' * API). */ fun getBiometricCancellationSignal(): CancellationSignal = uiState.biometricState.biometricCancellationSignal /** * This allows falling back from the biometric prompt screen to the normal get flow by applying * a reset to all necessary states involved in the fallback. Loading Loading @@ -450,9 +489,9 @@ class CredentialSelectorViewModel( } /** * This returns the present biometric state. * This returns the present biometric prompt state's status. */ fun getBiometricPromptState(): BiometricPromptState = fun getBiometricPromptStateStatus(): BiometricPromptState = uiState.biometricState.biometricStatus /**************************************************************************/ Loading packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt +28 −23 Original line number Diff line number Diff line Loading @@ -65,7 +65,6 @@ import java.lang.Exception * ). * * The above are examples; the credential type can change depending on scenario. * // TODO(b/333445112) : Finalize once all the strings and create flow is iterated to completion */ data class BiometricDisplayInfo( val providerIcon: Bitmap, Loading @@ -84,7 +83,8 @@ data class BiometricDisplayInfo( data class BiometricState( val biometricResult: BiometricResult? = null, val biometricError: BiometricError? = null, val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE, val biometricCancellationSignal: CancellationSignal = CancellationSignal(), ) /** Loading Loading @@ -118,6 +118,7 @@ fun runBiometricFlowForGet( getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, getRequestDisplayInfo: RequestDisplayInfo? = null, getProviderInfoList: List<ProviderInfo>? = null, getProviderDisplayInfo: ProviderDisplayInfo? = null, Loading @@ -141,11 +142,13 @@ fun runBiometricFlowForGet( val callback: BiometricPrompt.AuthenticationCallback = setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry, onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange) onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange, getBiometricPromptState) Log.d(TAG, "The BiometricPrompt API call begins.") Log.d(TAG, "The BiometricPrompt API call begins for Get.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish) onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish, getBiometricCancellationSignal) } /** Loading @@ -163,6 +166,7 @@ fun runBiometricFlowForCreate( getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, createRequestDisplayInfo: com.android.credentialmanager.createflow .RequestDisplayInfo? = null, createProviderInfo: EnabledProviderInfo? = null, Loading @@ -185,11 +189,13 @@ fun runBiometricFlowForCreate( val callback: BiometricPrompt.AuthenticationCallback = setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry, onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange) onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange, getBiometricPromptState) Log.d(TAG, "The BiometricPrompt API call begins.") Log.d(TAG, "The BiometricPrompt API call begins for Create.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish) onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish, getBiometricCancellationSignal) } /** Loading @@ -206,7 +212,8 @@ private fun runBiometricFlow( openMoreOptionsPage: () -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, biometricFlowType: BiometricFlowType, onCancelFlowAndFinish: () -> Unit onCancelFlowAndFinish: () -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, ) { try { if (!canCallBiometricPrompt(biometricDisplayInfo, context)) { Loading @@ -217,12 +224,7 @@ private fun runBiometricFlow( val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, openMoreOptionsPage, biometricDisplayInfo.biometricRequestInfo, onCancelFlowAndFinish) val cancellationSignal = CancellationSignal() cancellationSignal.setOnCancelListener { Log.d(TAG, "Your cancellation signal was called.") // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal // or validate the necessity for this } val cancellationSignal = getBiometricCancellationSignal() val executor = getMainExecutor(context) Loading Loading @@ -251,8 +253,6 @@ private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? { * Note that if device credential is the only available modality but not requested, or if none * 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 canCallBiometricPrompt( biometricDisplayInfo: BiometricDisplayInfo, Loading @@ -270,12 +270,12 @@ private fun canCallBiometricPrompt( return false } if (ifOnlySupportsAtMostDeviceCredentials(biometricManager)) return false if (onlySupportsAtMostDeviceCredentials(biometricManager)) return false return true } private fun ifOnlySupportsAtMostDeviceCredentials(biometricManager: BiometricManager): Boolean { private fun onlySupportsAtMostDeviceCredentials(biometricManager: BiometricManager): Boolean { if (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) != BiometricManager.BIOMETRIC_SUCCESS && biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) != Loading Loading @@ -343,11 +343,11 @@ private fun setupBiometricAuthenticationCallback( selectedEntry: EntryInfo, onCancelFlowAndFinish: () -> Unit, onIllegalStateAndFinish: (String) -> Unit, onBiometricPromptStateChange: (BiometricPromptState) -> Unit onBiometricPromptStateChange: (BiometricPromptState) -> Unit, getBiometricPromptState: () -> BiometricPromptState, ): BiometricPrompt.AuthenticationCallback { val callback: BiometricPrompt.AuthenticationCallback = object : BiometricPrompt.AuthenticationCallback() { // TODO(b/333445772) : Validate remaining callbacks override fun onAuthenticationSucceeded( authResult: BiometricPrompt.AuthenticationResult? ) { Loading @@ -374,6 +374,12 @@ private fun setupBiometricAuthenticationCallback( override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { super.onAuthenticationError(errorCode, errString) Log.d(TAG, "Authentication error-ed out: $errorCode and $errString") if (getBiometricPromptState() == BiometricPromptState.CANCELED && errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) { Log.d(TAG, "Developer cancellation signal received. Nothing more to do.") // This unique edge case means a developer cancellation signal was sent. return } onBiometricPromptStateChange(BiometricPromptState.COMPLETE) if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED) { // Note that because the biometric prompt is imbued directly Loading Loading @@ -471,8 +477,7 @@ private fun retrieveBiometricGetDisplayValues( val singleEntryType = selectedEntry.credentialType val username = selectedEntry.userName // TODO(b/330396140) : Finalize localization and parsing for specific sign in option flows // (fingerprint, face, etc...)) // TODO(b/336362538) : In W, utilize updated localization strings displayTitleText = context.getString( generateDisplayTitleTextResCode(singleEntryType), getRequestDisplayInfo.appName Loading packages/CredentialManager/src/com/android/credentialmanager/common/BiometricPromptState.kt +6 −1 Original line number Diff line number Diff line Loading @@ -22,5 +22,10 @@ enum class BiometricPromptState { /** The biometric prompt is active but data hasn't been returned yet. */ PENDING, /** The biometric prompt has closed and returned data we then send to the provider activity. */ COMPLETE COMPLETE, /** * The biometric prompt has been canceled by a developer signal. If this is true, certain * conditions can be triggered, such as no longer propagating ERROR_CANCELED. */ CANCELED, } No newline at end of file packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +7 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.credentialmanager.createflow import android.credentials.flags.Flags.selectorUiImprovementsEnabled import android.hardware.biometrics.BiometricPrompt import android.os.CancellationSignal import android.text.TextUtils import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult Loading Loading @@ -118,9 +119,11 @@ fun CreateCredentialScreen( fallbackToOriginalFlow = viewModel::fallbackFromBiometricToNormalFlow, getBiometricPromptState = viewModel::getBiometricPromptState, viewModel::getBiometricPromptStateStatus, onBiometricPromptStateChange = viewModel::onBiometricPromptStateChange viewModel::onBiometricPromptStateChange, getBiometricCancellationSignal = viewModel::getBiometricCancellationSignal ) CreateScreenState.MORE_OPTIONS_SELECTION_ONLY -> MoreOptionsSelectionCard( requestDisplayInfo = createCredentialUiState.requestDisplayInfo, Loading Loading @@ -638,6 +641,7 @@ internal fun BiometricSelectionPage( fallbackToOriginalFlow: (BiometricFlowType) -> Unit, getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, ) { if (biometricEntry == null) { fallbackToOriginalFlow(BiometricFlowType.CREATE) Loading @@ -655,5 +659,6 @@ internal fun BiometricSelectionPage( createProviderInfo = enabledProviderInfo, onBiometricFailureFallback = fallbackToOriginalFlow, onIllegalStateAndFinish = onIllegalScreenStateAndFinish, getBiometricCancellationSignal = getBiometricCancellationSignal, ) } Loading
packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +1 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,7 @@ class CredentialSelectorActivity : ComponentActivity() { return Triple(true, false, null) } val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationExplanation() viewModel?.onDeveloperCancellationReceivedForBiometricPrompt() Log.d( Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" + " ui = $shouldShowCancellationUi") Loading
packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +46 −7 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.credentialmanager import android.app.Activity import android.hardware.biometrics.BiometricPrompt import android.hardware.biometrics.BiometricPrompt.AuthenticationResult import android.os.CancellationSignal import android.os.IBinder import android.text.TextUtils import android.util.Log Loading Loading @@ -232,8 +233,13 @@ class CredentialSelectorViewModel( authResult: BiometricPrompt.AuthenticationResult? = null, authError: BiometricError? = null, ) { if (authError == null) { Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" + ", key=${entry.entryKey}, subkey=${entry.entrySubkey}}") } else { Log.d(Constants.LOG_TAG, "Biometric flow error: ${authError.errorCode} " + "propagating to provider, message: ${authError.errorMessage}.") } uiState = if (entry.pendingIntent != null) { uiState.copy( selectedEntry = entry, Loading Loading @@ -385,9 +391,15 @@ class CredentialSelectorViewModel( val providerId = selectedEntry.providerId val entryKey = selectedEntry.entryKey val entrySubkey = selectedEntry.entrySubkey if (authError == null) { Log.d( Constants.LOG_TAG, "Option selected for entry: " + " {provider=$providerId, key=$entryKey, subkey=$entrySubkey") " {provider=$providerId, key=$entryKey, subkey=$entrySubkey" ) } else { Log.d(Constants.LOG_TAG, "Biometric flow error: ${authError.errorCode} " + "propagating to provider, message: ${authError.errorMessage}.") } if (selectedEntry.pendingIntent != null) { uiState = uiState.copy( selectedEntry = selectedEntry, Loading Loading @@ -423,6 +435,33 @@ class CredentialSelectorViewModel( /***** Biometric Flow Callbacks *****/ /**************************************************************************/ /** * Cancels the biometric prompt's cancellation signal. Should only be called when the credential * manager ui receives a developer cancellation signal. If the prompt is already done, we do * not allow a cancellation, given the UI cancellation will be caught by the backend. We also * set the biometricStatus to CANCELED, so that only in this case, we do *not* propagate the * ERROR_CANCELED when a developer cancellation signal is the root cause. */ fun onDeveloperCancellationReceivedForBiometricPrompt() { val biometricCancellationSignal = uiState.biometricState.biometricCancellationSignal if (!biometricCancellationSignal.isCanceled && uiState.biometricState.biometricStatus != BiometricPromptState.COMPLETE) { uiState = uiState.copy( biometricState = uiState.biometricState.copy( biometricStatus = BiometricPromptState.CANCELED ) ) biometricCancellationSignal.cancel() } } /** * Retrieve the biometric prompt's cancellation signal (e.g. to pass into the 'authenticate' * API). */ fun getBiometricCancellationSignal(): CancellationSignal = uiState.biometricState.biometricCancellationSignal /** * This allows falling back from the biometric prompt screen to the normal get flow by applying * a reset to all necessary states involved in the fallback. Loading Loading @@ -450,9 +489,9 @@ class CredentialSelectorViewModel( } /** * This returns the present biometric state. * This returns the present biometric prompt state's status. */ fun getBiometricPromptState(): BiometricPromptState = fun getBiometricPromptStateStatus(): BiometricPromptState = uiState.biometricState.biometricStatus /**************************************************************************/ Loading
packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt +28 −23 Original line number Diff line number Diff line Loading @@ -65,7 +65,6 @@ import java.lang.Exception * ). * * The above are examples; the credential type can change depending on scenario. * // TODO(b/333445112) : Finalize once all the strings and create flow is iterated to completion */ data class BiometricDisplayInfo( val providerIcon: Bitmap, Loading @@ -84,7 +83,8 @@ data class BiometricDisplayInfo( data class BiometricState( val biometricResult: BiometricResult? = null, val biometricError: BiometricError? = null, val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE, val biometricCancellationSignal: CancellationSignal = CancellationSignal(), ) /** Loading Loading @@ -118,6 +118,7 @@ fun runBiometricFlowForGet( getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, getRequestDisplayInfo: RequestDisplayInfo? = null, getProviderInfoList: List<ProviderInfo>? = null, getProviderDisplayInfo: ProviderDisplayInfo? = null, Loading @@ -141,11 +142,13 @@ fun runBiometricFlowForGet( val callback: BiometricPrompt.AuthenticationCallback = setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry, onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange) onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange, getBiometricPromptState) Log.d(TAG, "The BiometricPrompt API call begins.") Log.d(TAG, "The BiometricPrompt API call begins for Get.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish) onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish, getBiometricCancellationSignal) } /** Loading @@ -163,6 +166,7 @@ fun runBiometricFlowForCreate( getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, createRequestDisplayInfo: com.android.credentialmanager.createflow .RequestDisplayInfo? = null, createProviderInfo: EnabledProviderInfo? = null, Loading @@ -185,11 +189,13 @@ fun runBiometricFlowForCreate( val callback: BiometricPrompt.AuthenticationCallback = setupBiometricAuthenticationCallback(sendDataToProvider, biometricEntry, onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange) onCancelFlowAndFinish, onIllegalStateAndFinish, onBiometricPromptStateChange, getBiometricPromptState) Log.d(TAG, "The BiometricPrompt API call begins.") Log.d(TAG, "The BiometricPrompt API call begins for Create.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish) onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish, getBiometricCancellationSignal) } /** Loading @@ -206,7 +212,8 @@ private fun runBiometricFlow( openMoreOptionsPage: () -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, biometricFlowType: BiometricFlowType, onCancelFlowAndFinish: () -> Unit onCancelFlowAndFinish: () -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, ) { try { if (!canCallBiometricPrompt(biometricDisplayInfo, context)) { Loading @@ -217,12 +224,7 @@ private fun runBiometricFlow( val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, openMoreOptionsPage, biometricDisplayInfo.biometricRequestInfo, onCancelFlowAndFinish) val cancellationSignal = CancellationSignal() cancellationSignal.setOnCancelListener { Log.d(TAG, "Your cancellation signal was called.") // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal // or validate the necessity for this } val cancellationSignal = getBiometricCancellationSignal() val executor = getMainExecutor(context) Loading Loading @@ -251,8 +253,6 @@ private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? { * Note that if device credential is the only available modality but not requested, or if none * 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 canCallBiometricPrompt( biometricDisplayInfo: BiometricDisplayInfo, Loading @@ -270,12 +270,12 @@ private fun canCallBiometricPrompt( return false } if (ifOnlySupportsAtMostDeviceCredentials(biometricManager)) return false if (onlySupportsAtMostDeviceCredentials(biometricManager)) return false return true } private fun ifOnlySupportsAtMostDeviceCredentials(biometricManager: BiometricManager): Boolean { private fun onlySupportsAtMostDeviceCredentials(biometricManager: BiometricManager): Boolean { if (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) != BiometricManager.BIOMETRIC_SUCCESS && biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) != Loading Loading @@ -343,11 +343,11 @@ private fun setupBiometricAuthenticationCallback( selectedEntry: EntryInfo, onCancelFlowAndFinish: () -> Unit, onIllegalStateAndFinish: (String) -> Unit, onBiometricPromptStateChange: (BiometricPromptState) -> Unit onBiometricPromptStateChange: (BiometricPromptState) -> Unit, getBiometricPromptState: () -> BiometricPromptState, ): BiometricPrompt.AuthenticationCallback { val callback: BiometricPrompt.AuthenticationCallback = object : BiometricPrompt.AuthenticationCallback() { // TODO(b/333445772) : Validate remaining callbacks override fun onAuthenticationSucceeded( authResult: BiometricPrompt.AuthenticationResult? ) { Loading @@ -374,6 +374,12 @@ private fun setupBiometricAuthenticationCallback( override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { super.onAuthenticationError(errorCode, errString) Log.d(TAG, "Authentication error-ed out: $errorCode and $errString") if (getBiometricPromptState() == BiometricPromptState.CANCELED && errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) { Log.d(TAG, "Developer cancellation signal received. Nothing more to do.") // This unique edge case means a developer cancellation signal was sent. return } onBiometricPromptStateChange(BiometricPromptState.COMPLETE) if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED) { // Note that because the biometric prompt is imbued directly Loading Loading @@ -471,8 +477,7 @@ private fun retrieveBiometricGetDisplayValues( val singleEntryType = selectedEntry.credentialType val username = selectedEntry.userName // TODO(b/330396140) : Finalize localization and parsing for specific sign in option flows // (fingerprint, face, etc...)) // TODO(b/336362538) : In W, utilize updated localization strings displayTitleText = context.getString( generateDisplayTitleTextResCode(singleEntryType), getRequestDisplayInfo.appName Loading
packages/CredentialManager/src/com/android/credentialmanager/common/BiometricPromptState.kt +6 −1 Original line number Diff line number Diff line Loading @@ -22,5 +22,10 @@ enum class BiometricPromptState { /** The biometric prompt is active but data hasn't been returned yet. */ PENDING, /** The biometric prompt has closed and returned data we then send to the provider activity. */ COMPLETE COMPLETE, /** * The biometric prompt has been canceled by a developer signal. If this is true, certain * conditions can be triggered, such as no longer propagating ERROR_CANCELED. */ CANCELED, } No newline at end of file
packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +7 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.credentialmanager.createflow import android.credentials.flags.Flags.selectorUiImprovementsEnabled import android.hardware.biometrics.BiometricPrompt import android.os.CancellationSignal import android.text.TextUtils import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult Loading Loading @@ -118,9 +119,11 @@ fun CreateCredentialScreen( fallbackToOriginalFlow = viewModel::fallbackFromBiometricToNormalFlow, getBiometricPromptState = viewModel::getBiometricPromptState, viewModel::getBiometricPromptStateStatus, onBiometricPromptStateChange = viewModel::onBiometricPromptStateChange viewModel::onBiometricPromptStateChange, getBiometricCancellationSignal = viewModel::getBiometricCancellationSignal ) CreateScreenState.MORE_OPTIONS_SELECTION_ONLY -> MoreOptionsSelectionCard( requestDisplayInfo = createCredentialUiState.requestDisplayInfo, Loading Loading @@ -638,6 +641,7 @@ internal fun BiometricSelectionPage( fallbackToOriginalFlow: (BiometricFlowType) -> Unit, getBiometricPromptState: () -> BiometricPromptState, onBiometricPromptStateChange: (BiometricPromptState) -> Unit, getBiometricCancellationSignal: () -> CancellationSignal, ) { if (biometricEntry == null) { fallbackToOriginalFlow(BiometricFlowType.CREATE) Loading @@ -655,5 +659,6 @@ internal fun BiometricSelectionPage( createProviderInfo = enabledProviderInfo, onBiometricFailureFallback = fallbackToOriginalFlow, onIllegalStateAndFinish = onIllegalScreenStateAndFinish, getBiometricCancellationSignal = getBiometricCancellationSignal, ) }