Merge "Prioritize Credential Types based on priority set by jetpack library" into main
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index 83490b8..b435bb8 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -30,6 +30,7 @@
import android.text.TextUtils
import android.util.Log
import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PasswordCredential
import androidx.credentials.PublicKeyCredential
import androidx.credentials.provider.Action
import androidx.credentials.provider.AuthenticationAction
@@ -125,6 +126,7 @@
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.PASSWORD,
+ rawCredentialType = PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
@@ -149,6 +151,7 @@
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.PASSKEY,
+ rawCredentialType = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
@@ -175,6 +178,7 @@
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.UNKNOWN,
+ rawCredentialType = credentialEntry.type,
credentialTypeDisplayName =
credentialEntry.typeDisplayName?.toString().orEmpty(),
userName = credentialEntry.title.toString(),
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
index 9f36096..ac42b60 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -31,6 +31,11 @@
fillInIntent: Intent?,
/** Type of this credential used for sorting. Not localized so must not be directly displayed. */
val credentialType: CredentialType,
+ /**
+ * String type value of this credential used for sorting. Not localized so must not be directly
+ * displayed.
+ */
+ val rawCredentialType: String,
/** Localized type value of this credential used for display purpose. */
val credentialTypeDisplayName: String,
val providerDisplayName: String,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index ccf401d..6a1998a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
+import android.credentials.GetCredentialRequest
import android.credentials.selection.CreateCredentialProviderData
import android.credentials.selection.DisabledProviderData
import android.credentials.selection.Entry
@@ -44,6 +45,9 @@
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.PasswordCredential
+import androidx.credentials.PriorityHints
+import androidx.credentials.PublicKeyCredential
import androidx.credentials.provider.CreateEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
@@ -162,6 +166,25 @@
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
companion object {
+ fun extractTypePriorityMap(request: GetCredentialRequest): Map<String, Int> {
+ val typePriorityMap = mutableMapOf<String, Int>()
+ request.credentialOptions.forEach {option ->
+ // TODO(b/280085288) - use jetpack conversion method when exposed, rather than
+ // parsing from the raw Bundle
+ val priority = option.candidateQueryData.getInt(
+ "androidx.credentials.BUNDLE_KEY_TYPE_PRIORITY_VALUE",
+ when (option.type) {
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL ->
+ PriorityHints.PRIORITY_PASSWORD_OR_SIMILAR
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> 100
+ else -> PriorityHints.PRIORITY_DEFAULT
+ }
+ )
+ typePriorityMap[option.type] = priority
+ }
+ return typePriorityMap
+ }
+
// Returns the list (potentially empty) of enabled provider.
fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
@@ -193,6 +216,9 @@
null
}
}
+
+ val typePriorityMap = extractTypePriorityMap(getCredentialRequest)
+
return com.android.credentialmanager.getflow.RequestDisplayInfo(
appName = originName?.ifEmpty { null }
?: getAppLabel(context.packageManager, requestInfo.packageName)
@@ -203,6 +229,7 @@
// exposed.
"androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"),
preferTopBrandingContent = preferTopBrandingContent,
+ typePriorityMap = typePriorityMap,
)
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 293e111..4e1f4ee 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -33,7 +33,6 @@
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
-import android.provider.Settings
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -140,7 +139,7 @@
override fun onResult(result: GetCandidateCredentialsResponse) {
Log.i(TAG, "getCandidateCredentials onResult")
val fillResponse = convertToFillResponse(result, request,
- responseClientState)
+ responseClientState, GetFlowUtils.extractTypePriorityMap(getCredRequest))
if (fillResponse != null) {
callback.onSuccess(fillResponse)
} else {
@@ -197,7 +196,8 @@
private fun convertToFillResponse(
getCredResponse: GetCandidateCredentialsResponse,
filLRequest: FillRequest,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ typePriorityMap: Map<String, Int>,
): FillResponse? {
val candidateProviders = getCredResponse.candidateProviderDataList
if (candidateProviders.isEmpty()) {
@@ -213,7 +213,7 @@
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
validFillResponse = processProvidersForAutofillId(
filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
- getCredResponse.intent)
+ getCredResponse.intent, typePriorityMap)
.or(validFillResponse)
}
if (!validFillResponse) {
@@ -229,7 +229,8 @@
providerDataList: ArrayList<GetCredentialProviderData>,
entryIconMap: Map<String, Icon>,
fillResponseBuilder: FillResponse.Builder,
- bottomSheetIntent: Intent
+ bottomSheetIntent: Intent,
+ typePriorityMap: Map<String, Int>,
): Boolean {
val providerList = GetFlowUtils.toProviderList(
providerDataList,
@@ -237,7 +238,8 @@
if (providerList.isEmpty()) {
return false
}
- val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
+ val providerDisplayInfo: ProviderDisplayInfo =
+ toProviderDisplayInfo(providerList, typePriorityMap)
var totalEntryCount = providerDisplayInfo.sortedUserNameToCredentialEntryList.size
val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index e7f11a1..ef40188 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -18,9 +18,9 @@
import android.credentials.flags.Flags.selectorUiImprovementsEnabled
import android.graphics.drawable.Drawable
+import androidx.credentials.PriorityHints
import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.model.EntryInfo
-import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.model.get.RemoteEntryInfo
@@ -30,7 +30,8 @@
val isRequestForAllOptions: Boolean,
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
- val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
+ val providerDisplayInfo: ProviderDisplayInfo =
+ toProviderDisplayInfo(providerInfoList, requestDisplayInfo.typePriorityMap),
val currentScreenState: GetScreenState = toGetScreenState(
providerDisplayInfo, isRequestForAllOptions),
val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
@@ -79,6 +80,8 @@
val preferIdentityDocUi: Boolean,
// A top level branding icon + display name preferred by the app.
val preferTopBrandingContent: TopBrandingContent?,
+ // Map of credential type -> priority.
+ val typePriorityMap: Map<String, Int>,
)
data class TopBrandingContent(
@@ -119,7 +122,8 @@
* @hide
*/
fun toProviderDisplayInfo(
- providerInfoList: List<ProviderInfo>
+ providerInfoList: List<ProviderInfo>,
+ typePriorityMap: Map<String, Int>,
): ProviderDisplayInfo {
val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
@@ -147,7 +151,7 @@
}
// Compose sortedUserNameToCredentialEntryList
- val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp()
+ val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp(typePriorityMap)
// Sort per username
userNameToCredentialEntryMap.values.forEach {
it.sortWith(comparator)
@@ -203,13 +207,21 @@
else GetScreenState.PRIMARY_SELECTION
}
-internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<CredentialEntryInfo> {
+internal class CredentialEntryInfoComparatorByTypeThenTimestamp(
+ val typePriorityMap: Map<String, Int>,
+) : Comparator<CredentialEntryInfo> {
override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
// First prefer passkey type for its security benefits
- if (p0.credentialType != p1.credentialType) {
- if (CredentialType.PASSKEY == p0.credentialType) {
+ if (p0.rawCredentialType != p1.rawCredentialType) {
+ val p0Priority = typePriorityMap.getOrDefault(
+ p0.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+ )
+ val p1Priority = typePriorityMap.getOrDefault(
+ p1.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+ )
+ if (p0Priority < p1Priority) {
return -1
- } else if (CredentialType.PASSKEY == p1.credentialType) {
+ } else if (p1Priority < p0Priority) {
return 1
}
}
diff --git a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
index 94217d0..0820d26 100644
--- a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
+++ b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
@@ -53,6 +53,7 @@
preferImmediatelyAvailableCredentials = false,
preferIdentityDocUi = false,
preferTopBrandingContent = null,
+ typePriorityMap = emptyMap(),
)
}
@@ -68,7 +69,7 @@
fun singleCredentialScreen_M3BottomSheetDisabled() {
setFlagsRule.disableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED)
val providerInfoList = buildProviderInfoList()
- val providerDisplayInfo = toProviderDisplayInfo(providerInfoList)
+ val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap())
val activeEntry = toActiveEntry(providerDisplayInfo)
screenshotRule.screenshotTest("singleCredentialScreen") {
ModalBottomSheet(
@@ -96,7 +97,7 @@
fun singleCredentialScreen_M3BottomSheetEnabled() {
setFlagsRule.enableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED)
val providerInfoList = buildProviderInfoList()
- val providerDisplayInfo = toProviderDisplayInfo(providerInfoList)
+ val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap())
val activeEntry = toActiveEntry(providerDisplayInfo)
screenshotRule.screenshotTest(
"singleCredentialScreen_newM3BottomSheet",
@@ -149,6 +150,7 @@
isAutoSelectable = false,
entryGroupId = "username",
isDefaultIconPreferredAsSingleProvider = false,
+ rawCredentialType = "unknown-type",
)
),
authenticationEntryList = emptyList(),