Fix Error Propagation and No Device Lock
Alongside the CL where we polish the UX to be updated with the mock, this CL aims to
finalize a specific edge case - the lack of a device credential. This is
done and also realizes a second goal - ensuring error propagation for
any non-cancellation based error, as per earlier product decisions.
Bug: 331826599
Bug: 333445754
Test: Video and build tests found in bug
Change-Id: I1015c4142703f3284bdf0e9bac78301ac1d6341a
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 888777e..36f6ad2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -30,6 +30,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
+import com.android.credentialmanager.common.BiometricError
import com.android.credentialmanager.common.BiometricFlowType
import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.common.BiometricResult
@@ -128,13 +129,22 @@
uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
val entryIntent = entry.fillInIntent
entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
- if (biometricState.biometricResult != null) {
+ if (biometricState.biometricResult != null || biometricState.biometricError != null) {
if (uiState.isAutoSelectFlow) {
Log.w(Constants.LOG_TAG, "Unexpected biometric result exists when " +
"autoSelect is preferred.")
}
- entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_TYPE,
- biometricState.biometricResult.biometricAuthenticationResult.authenticationType)
+ // TODO(b/333445754) : Decide whether to propagate info on prompt launch
+ if (biometricState.biometricResult != null) {
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_RESULT,
+ biometricState.biometricResult.biometricAuthenticationResult
+ .authenticationType)
+ } else if (biometricState.biometricError != null){
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_CODE,
+ biometricState.biometricError.errorCode)
+ entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_MESSAGE,
+ biometricState.biometricError.errorMessage)
+ }
}
val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
.setFillInIntent(entryIntent).build()
@@ -219,7 +229,8 @@
/**************************************************************************/
fun getFlowOnEntrySelected(
entry: EntryInfo,
- authResult: BiometricPrompt.AuthenticationResult? = null
+ authResult: BiometricPrompt.AuthenticationResult? = null,
+ authError: BiometricError? = null,
) {
Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
@@ -227,10 +238,11 @@
uiState.copy(
selectedEntry = entry,
providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
- biometricState = if (authResult == null) uiState.biometricState else uiState
+ biometricState = if (authResult == null && authError == null)
+ uiState.biometricState else if (authResult != null) uiState
.biometricState.copy(biometricResult = BiometricResult(
- biometricAuthenticationResult = authResult)
- )
+ biometricAuthenticationResult = authResult)) else uiState
+ .biometricState.copy(biometricError = authError)
)
} else {
credManRepo.onOptionSelected(entry.providerId, entry.entryKey, entry.entrySubkey)
@@ -350,7 +362,8 @@
fun createFlowOnEntrySelected(
selectedEntry: EntryInfo,
- authResult: AuthenticationResult? = null
+ authResult: AuthenticationResult? = null,
+ authError: BiometricError? = null,
) {
val providerId = selectedEntry.providerId
val entryKey = selectedEntry.entryKey
@@ -362,9 +375,11 @@
uiState = uiState.copy(
selectedEntry = selectedEntry,
providerActivityState = ProviderActivityState.READY_TO_LAUNCH,
- biometricState = if (authResult == null) uiState.biometricState else uiState
+ biometricState = if (authResult == null && authError == null)
+ uiState.biometricState else if (authResult != null) uiState
.biometricState.copy(biometricResult = BiometricResult(
- biometricAuthenticationResult = authResult))
+ biometricAuthenticationResult = authResult)) else uiState
+ .biometricState.copy(biometricError = authError)
)
} else {
credManRepo.onOptionSelected(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index be3e043..e088d3a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -76,6 +76,7 @@
*/
data class BiometricState(
val biometricResult: BiometricResult? = null,
+ val biometricError: BiometricError? = null,
val biometricStatus: BiometricPromptState = BiometricPromptState.INACTIVE
)
@@ -92,7 +93,7 @@
*/
data class BiometricError(
val errorCode: Int,
- val errString: CharSequence? = null
+ val errorMessage: CharSequence? = null
)
/**
@@ -113,7 +114,7 @@
biometricEntry: EntryInfo,
context: Context,
openMoreOptionsPage: () -> Unit,
- sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
onCancelFlowAndFinish: () -> Unit,
onIllegalStateAndFinish: (String) -> Unit,
getBiometricPromptState: () -> BiometricPromptState,
@@ -158,7 +159,7 @@
biometricEntry: EntryInfo,
context: Context,
openMoreOptionsPage: () -> Unit,
- sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
onCancelFlowAndFinish: () -> Unit,
onIllegalStateAndFinish: (String) -> Unit,
getBiometricPromptState: () -> BiometricPromptState,
@@ -285,7 +286,7 @@
* Sets up the biometric authentication callback.
*/
private fun setupBiometricAuthenticationCallback(
- sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ sendDataToProvider: (EntryInfo, BiometricPrompt.AuthenticationResult?, BiometricError?) -> Unit,
selectedEntry: EntryInfo,
onCancelFlowAndFinish: () -> Unit,
onIllegalStateAndFinish: (String) -> Unit,
@@ -301,7 +302,7 @@
try {
if (authResult != null) {
onBiometricPromptStateChange(BiometricPromptState.COMPLETE)
- sendDataToProvider(selectedEntry, authResult)
+ sendDataToProvider(selectedEntry, authResult, /*authError=*/null)
} else {
onIllegalStateAndFinish("The biometric flow succeeded but unexpectedly " +
"returned a null value.")
@@ -326,8 +327,10 @@
// into the selector, parity applies to the selector's cancellation instead
// of the provider's biometric prompt cancellation.
onCancelFlowAndFinish()
+ } else {
+ sendDataToProvider(selectedEntry, /*authResult=*/null, /*authError=*/
+ BiometricError(errorCode, errString))
}
- // TODO(b/333445772) : Propagate to provider
}
override fun onAuthenticationFailed() {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 7e7a74f..3c80113 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -22,7 +22,9 @@
const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
- const val BIOMETRIC_AUTH_TYPE = "BIOMETRIC_AUTH_TYPE"
- const val BIOMETRIC_AUTH_FAILURE = "BIOMETRIC_AUTH_FAILURE"
+ // TODO(b/333445772) : Qualify error codes fully for propagation
+ const val BIOMETRIC_AUTH_RESULT = "BIOMETRIC_AUTH_RESULT"
+ const val BIOMETRIC_AUTH_ERROR_CODE = "BIOMETRIC_AUTH_ERROR_CODE"
+ const val BIOMETRIC_AUTH_ERROR_MESSAGE = "BIOMETRIC_AUTH_ERROR_MESSAGE"
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 122b896..a0915d2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -46,6 +46,7 @@
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.BiometricError
import com.android.credentialmanager.common.BiometricFlowType
import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.model.EntryInfo
@@ -581,7 +582,11 @@
onMoreOptionSelected: () -> Unit,
requestDisplayInfo: RequestDisplayInfo,
enabledProviderInfo: EnabledProviderInfo,
- onBiometricEntrySelected: (EntryInfo, BiometricPrompt.AuthenticationResult) -> Unit,
+ onBiometricEntrySelected: (
+ EntryInfo,
+ BiometricPrompt.AuthenticationResult?,
+ BiometricError?
+ ) -> Unit,
onCancelFlowAndFinish: () -> Unit,
onIllegalScreenStateAndFinish: (String) -> Unit,
fallbackToOriginalFlow: (BiometricFlowType) -> Unit,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 72b7814..c1120bb3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -52,6 +52,7 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
+import com.android.credentialmanager.common.BiometricError
import com.android.credentialmanager.common.BiometricFlowType
import com.android.credentialmanager.common.BiometricPromptState
import com.android.credentialmanager.common.ProviderActivityState
@@ -223,7 +224,11 @@
requestDisplayInfo: RequestDisplayInfo,
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
- onBiometricEntrySelected: (EntryInfo, BiometricPrompt.AuthenticationResult?) -> Unit,
+ onBiometricEntrySelected: (
+ EntryInfo,
+ BiometricPrompt.AuthenticationResult?,
+ BiometricError?
+ ) -> Unit,
fallbackToOriginalFlow: (BiometricFlowType) -> Unit,
getBiometricPromptState: () -> BiometricPromptState,
onBiometricPromptStateChange: (BiometricPromptState) -> Unit,