Remove test data fallback from the UI app.
Also discovered a bug around parsing the cancel request, see the one line change in CredentialSelectorActivity.kt. Test: local e2e verification Bug: 265041783 Change-Id: I25f01613be1a636736ce738b067f9467ab2d6cc7
This commit is contained in:
@@ -18,14 +18,8 @@ package com.android.credentialmanager
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.credentials.CreateCredentialRequest
|
||||
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
|
||||
import android.credentials.CredentialOption
|
||||
import android.credentials.GetCredentialRequest
|
||||
import android.credentials.ui.AuthenticationEntry
|
||||
import android.credentials.ui.CancelUiRequest
|
||||
import android.credentials.ui.Constants
|
||||
import android.credentials.ui.Entry
|
||||
import android.credentials.ui.CreateCredentialProviderData
|
||||
import android.credentials.ui.GetCredentialProviderData
|
||||
import android.credentials.ui.DisabledProviderData
|
||||
@@ -35,23 +29,16 @@ import android.credentials.ui.BaseDialogResult
|
||||
import android.credentials.ui.ProviderPendingIntentResponse
|
||||
import android.credentials.ui.UserSelectionDialogResult
|
||||
import android.os.IBinder
|
||||
import android.os.Binder
|
||||
import android.os.Bundle
|
||||
import android.os.ResultReceiver
|
||||
import android.util.Log
|
||||
import com.android.credentialmanager.createflow.DisabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.EnabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.RequestDisplayInfo
|
||||
import com.android.credentialmanager.getflow.GetCredentialUiState
|
||||
import com.android.credentialmanager.getflow.findAutoSelectEntry
|
||||
import androidx.credentials.CreateCredentialRequest.DisplayInfo
|
||||
import androidx.credentials.CreatePublicKeyCredentialRequest
|
||||
import androidx.credentials.CreatePasswordRequest
|
||||
import androidx.credentials.GetPasswordOption
|
||||
import androidx.credentials.GetPublicKeyCredentialOption
|
||||
import com.android.credentialmanager.common.ProviderActivityState
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Client for interacting with Credential Manager. Also holds data inputs from it.
|
||||
*
|
||||
@@ -63,7 +50,7 @@ class CredentialManagerRepo(
|
||||
intent: Intent,
|
||||
userConfigRepo: UserConfigRepo,
|
||||
) {
|
||||
val requestInfo: RequestInfo
|
||||
val requestInfo: RequestInfo?
|
||||
private val providerEnabledList: List<ProviderData>
|
||||
private val providerDisabledList: List<DisabledProviderData>?
|
||||
// TODO: require non-null.
|
||||
@@ -75,27 +62,30 @@ class CredentialManagerRepo(
|
||||
requestInfo = intent.extras?.getParcelable(
|
||||
RequestInfo.EXTRA_REQUEST_INFO,
|
||||
RequestInfo::class.java
|
||||
) ?: testGetRequestInfo()
|
||||
)
|
||||
|
||||
val originName: String? = when (requestInfo.type) {
|
||||
RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin
|
||||
RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin
|
||||
val originName: String? = when (requestInfo?.type) {
|
||||
RequestInfo.TYPE_CREATE -> requestInfo?.createCredentialRequest?.origin
|
||||
RequestInfo.TYPE_GET -> requestInfo?.getCredentialRequest?.origin
|
||||
else -> null
|
||||
}
|
||||
|
||||
providerEnabledList = when (requestInfo.type) {
|
||||
providerEnabledList = when (requestInfo?.type) {
|
||||
RequestInfo.TYPE_CREATE ->
|
||||
intent.extras?.getParcelableArrayList(
|
||||
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
|
||||
CreateCredentialProviderData::class.java
|
||||
) ?: testCreateCredentialEnabledProviderList()
|
||||
) ?: emptyList()
|
||||
RequestInfo.TYPE_GET ->
|
||||
intent.extras?.getParcelableArrayList(
|
||||
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
|
||||
GetCredentialProviderData::class.java
|
||||
) ?: testGetCredentialProviderList()
|
||||
) ?: emptyList()
|
||||
else -> {
|
||||
throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
|
||||
Log.d(
|
||||
com.android.credentialmanager.common.Constants.LOG_TAG,
|
||||
"Unrecognized request type: ${requestInfo?.type}")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +93,7 @@ class CredentialManagerRepo(
|
||||
intent.extras?.getParcelableArrayList(
|
||||
ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST,
|
||||
DisabledProviderData::class.java
|
||||
) ?: testDisabledProviderList()
|
||||
)
|
||||
|
||||
resultReceiver = intent.getParcelableExtra(
|
||||
Constants.EXTRA_RESULT_RECEIVER,
|
||||
@@ -115,7 +105,7 @@ class CredentialManagerRepo(
|
||||
CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
|
||||
}
|
||||
|
||||
initialUiState = when (requestInfo.type) {
|
||||
initialUiState = when (requestInfo?.type) {
|
||||
RequestInfo.TYPE_CREATE -> {
|
||||
val defaultProviderId = userConfigRepo.getDefaultProviderId()
|
||||
val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
|
||||
@@ -151,7 +141,17 @@ class CredentialManagerRepo(
|
||||
cancelRequestState = cancelUiRequestState
|
||||
)
|
||||
}
|
||||
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
|
||||
else -> {
|
||||
if (cancellationRequest != null) {
|
||||
UiState(
|
||||
createCredentialUiState = null,
|
||||
getCredentialUiState = null,
|
||||
cancelRequestState = cancelUiRequestState,
|
||||
)
|
||||
} else {
|
||||
throw IllegalStateException("Unrecognized request type: ${requestInfo?.type}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class CredentialManagerRepo(
|
||||
}
|
||||
|
||||
fun onCancel(cancelCode: Int) {
|
||||
sendCancellationCode(cancelCode, requestInfo.token, resultReceiver)
|
||||
sendCancellationCode(cancelCode, requestInfo?.token, resultReceiver)
|
||||
}
|
||||
|
||||
fun onOptionSelected(
|
||||
@@ -185,7 +185,7 @@ class CredentialManagerRepo(
|
||||
resultData: Intent? = null,
|
||||
) {
|
||||
val userSelectionDialogResult = UserSelectionDialogResult(
|
||||
requestInfo.token,
|
||||
requestInfo?.token,
|
||||
providerId,
|
||||
entryKey,
|
||||
entrySubkey,
|
||||
@@ -213,10 +213,9 @@ class CredentialManagerRepo(
|
||||
|
||||
// IMPORTANT: new invocation should be mindful that this method can throw.
|
||||
private fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
|
||||
val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
|
||||
return CreateFlowUtils.toEnabledProviderList(
|
||||
providerEnabledList as List<CreateCredentialProviderData>, context
|
||||
)
|
||||
return providerEnabledList
|
||||
}
|
||||
|
||||
private fun getCreateProviderDisableListInitialUiState(): List<DisabledProviderInfo> {
|
||||
@@ -253,242 +252,4 @@ class CredentialManagerRepo(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: below are prototype functionalities. To be removed for productionization.
|
||||
private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
|
||||
return listOf(
|
||||
CreateCredentialProviderData
|
||||
.Builder("io.enpass.app")
|
||||
.setSaveEntries(
|
||||
listOf<Entry>(
|
||||
CreateTestUtils.newCreateEntry(
|
||||
context,
|
||||
"key1", "subkey-1", "elisa.beckett@gmail.com",
|
||||
20, 7, 27, Instant.ofEpochSecond(10L),
|
||||
"You can use your passkey on this or other devices. It is saved to " +
|
||||
"the Password Manager for elisa.beckett@gmail.com."
|
||||
),
|
||||
CreateTestUtils.newCreateEntry(
|
||||
context,
|
||||
"key1", "subkey-2", "elisa.work@google.com",
|
||||
20, 7, 27, Instant.ofEpochSecond(12L),
|
||||
null
|
||||
),
|
||||
)
|
||||
).setRemoteEntry(
|
||||
CreateTestUtils.newRemoteCreateEntry(context, "key2", "subkey-1")
|
||||
).build(),
|
||||
CreateCredentialProviderData
|
||||
.Builder("com.dashlane")
|
||||
.setSaveEntries(
|
||||
listOf<Entry>(
|
||||
CreateTestUtils.newCreateEntry(
|
||||
context,
|
||||
"key1", "subkey-3", "elisa.beckett@dashlane.com",
|
||||
20, 7, 27, Instant.ofEpochSecond(11L),
|
||||
null
|
||||
),
|
||||
CreateTestUtils.newCreateEntry(
|
||||
context,
|
||||
"key1", "subkey-4", "elisa.work@dashlane.com",
|
||||
20, 7, 27, Instant.ofEpochSecond(14L),
|
||||
"You can use your passkey on this or other devices. It is saved to " +
|
||||
"the Password Manager for elisa.work@dashlane.com"
|
||||
),
|
||||
)
|
||||
).build(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun testDisabledProviderList(): List<DisabledProviderData>? {
|
||||
return listOf(
|
||||
DisabledProviderData("com.lastpass.lpandroid"),
|
||||
DisabledProviderData("com.google.android.youtube")
|
||||
)
|
||||
}
|
||||
|
||||
private fun testGetCredentialProviderList(): List<GetCredentialProviderData> {
|
||||
return listOf(
|
||||
GetCredentialProviderData.Builder("io.enpass.app")
|
||||
.setCredentialEntries(
|
||||
listOf(
|
||||
GetTestUtils.newPasswordEntry(
|
||||
context, "key1", "subkey-1", "elisa.family@outlook.com", null,
|
||||
Instant.ofEpochSecond(8000L)
|
||||
),
|
||||
GetTestUtils.newPasskeyEntry(
|
||||
context, "key1", "subkey-1", "elisa.bakery@gmail.com", "Elisa Beckett",
|
||||
null
|
||||
),
|
||||
GetTestUtils.newPasswordEntry(
|
||||
context, "key1", "subkey-2", "elisa.bakery@gmail.com", null,
|
||||
Instant.ofEpochSecond(10000L)
|
||||
),
|
||||
GetTestUtils.newPasskeyEntry(
|
||||
context, "key1", "subkey-3", "elisa.family@outlook.com",
|
||||
"Elisa Beckett", Instant.ofEpochSecond(500L)
|
||||
),
|
||||
)
|
||||
).setAuthenticationEntries(
|
||||
listOf(
|
||||
GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-1", "locked-user1@gmail.com",
|
||||
AuthenticationEntry.STATUS_LOCKED
|
||||
),
|
||||
GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-2", "locked-user2@gmail.com",
|
||||
AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
|
||||
),
|
||||
)
|
||||
).setActionChips(
|
||||
listOf(
|
||||
GetTestUtils.newActionEntry(
|
||||
context, "key3", "subkey-1",
|
||||
"Open Google Password Manager", "elisa.beckett@gmail.com"
|
||||
),
|
||||
GetTestUtils.newActionEntry(
|
||||
context, "key3", "subkey-2",
|
||||
"Open Google Password Manager", "beckett-family@gmail.com"
|
||||
),
|
||||
)
|
||||
).setRemoteEntry(
|
||||
GetTestUtils.newRemoteCredentialEntry(context, "key4", "subkey-1")
|
||||
).build(),
|
||||
GetCredentialProviderData.Builder("com.dashlane")
|
||||
.setCredentialEntries(
|
||||
listOf<Entry>(
|
||||
GetTestUtils.newPasswordEntry(
|
||||
context, "key1", "subkey-2", "elisa.family@outlook.com", null,
|
||||
Instant.ofEpochSecond(9000L)
|
||||
),
|
||||
GetTestUtils.newPasswordEntry(
|
||||
context, "key1", "subkey-3", "elisa.work@outlook.com", null,
|
||||
Instant.ofEpochSecond(11000L)
|
||||
),
|
||||
)
|
||||
).setAuthenticationEntries(
|
||||
listOf(
|
||||
GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-1", "foo@email.com",
|
||||
AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT,
|
||||
)
|
||||
)
|
||||
).setActionChips(
|
||||
listOf(
|
||||
GetTestUtils.newActionEntry(
|
||||
context, "key3", "subkey-1", "Open Enpass",
|
||||
"Manage passwords"
|
||||
),
|
||||
)
|
||||
).build(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun testCreatePasskeyRequestInfo(): RequestInfo {
|
||||
val request = CreatePublicKeyCredentialRequest(
|
||||
"{\"extensions\": {\n" +
|
||||
" \"webauthn.loc\": true\n" +
|
||||
" },\n" +
|
||||
" \"attestation\": \"direct\",\n" +
|
||||
" \"challenge\":" +
|
||||
" \"-rSQHXSQUdaK1N-La5bE-JPt6EVAW4SxX1K_tXhZ_Gk\",\n" +
|
||||
" \"user\": {\n" +
|
||||
" \"displayName\": \"testName\",\n" +
|
||||
" \"name\": \"credManTesting@gmail.com\",\n" +
|
||||
" \"id\": \"eD4o2KoXLpgegAtnM5cDhhUPvvk2\"\n" +
|
||||
" },\n" +
|
||||
" \"excludeCredentials\": [],\n" +
|
||||
" \"rp\": {\n" +
|
||||
" \"name\": \"Address Book\",\n" +
|
||||
" \"id\": \"addressbook-c7876.uc.r.appspot.com\"\n" +
|
||||
" },\n" +
|
||||
" \"timeout\": 60000,\n" +
|
||||
" \"pubKeyCredParams\": [\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"public-key\",\n" +
|
||||
" \"alg\": -7\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"public-key\",\n" +
|
||||
" \"alg\": -257\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"public-key\",\n" +
|
||||
" \"alg\": -37\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"authenticatorSelection\": {\n" +
|
||||
" \"residentKey\": \"required\",\n" +
|
||||
" \"requireResidentKey\": true\n" +
|
||||
" }}",
|
||||
preferImmediatelyAvailableCredentials = true,
|
||||
)
|
||||
val credentialData = request.credentialData
|
||||
return RequestInfo.newCreateRequestInfo(
|
||||
Binder(),
|
||||
CreateCredentialRequest.Builder("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
|
||||
credentialData, Bundle())
|
||||
.setIsSystemProviderRequired(false)
|
||||
.setAlwaysSendAppInfoToProvider(true)
|
||||
.build(),
|
||||
"com.google.android.youtube"
|
||||
)
|
||||
}
|
||||
|
||||
private fun testCreatePasswordRequestInfo(): RequestInfo {
|
||||
val request = CreatePasswordRequest("beckett-bakert@gmail.com", "password123")
|
||||
return RequestInfo.newCreateRequestInfo(
|
||||
Binder(),
|
||||
CreateCredentialRequest.Builder(TYPE_PASSWORD_CREDENTIAL,
|
||||
request.credentialData, request.candidateQueryData)
|
||||
.setIsSystemProviderRequired(false)
|
||||
.setAlwaysSendAppInfoToProvider(true)
|
||||
.build(),
|
||||
"com.google.android.youtube"
|
||||
)
|
||||
}
|
||||
|
||||
private fun testCreateOtherCredentialRequestInfo(): RequestInfo {
|
||||
val data = Bundle()
|
||||
val displayInfo = DisplayInfo("my-username00", "Joe")
|
||||
data.putBundle(
|
||||
"androidx.credentials.BUNDLE_KEY_REQUEST_DISPLAY_INFO",
|
||||
displayInfo.toBundle()
|
||||
)
|
||||
return RequestInfo.newCreateRequestInfo(
|
||||
Binder(),
|
||||
CreateCredentialRequest.Builder("other-sign-ins", data, Bundle())
|
||||
.setIsSystemProviderRequired(false)
|
||||
.setAlwaysSendAppInfoToProvider(true)
|
||||
.build(),
|
||||
"com.google.android.youtube"
|
||||
)
|
||||
}
|
||||
|
||||
private fun testGetRequestInfo(): RequestInfo {
|
||||
val passwordOption = GetPasswordOption()
|
||||
val passkeyOption = GetPublicKeyCredentialOption(
|
||||
"json", preferImmediatelyAvailableCredentials = false)
|
||||
return RequestInfo.newGetRequestInfo(
|
||||
Binder(),
|
||||
GetCredentialRequest.Builder(
|
||||
Bundle()
|
||||
).addCredentialOption(
|
||||
CredentialOption.Builder(
|
||||
passwordOption.type,
|
||||
passwordOption.requestData,
|
||||
passwordOption.candidateQueryData,
|
||||
).setIsSystemProviderRequired(passwordOption.isSystemProviderRequired)
|
||||
.build()
|
||||
).addCredentialOption(
|
||||
CredentialOption.Builder(
|
||||
passkeyOption.type,
|
||||
passkeyOption.requestData,
|
||||
passkeyOption.candidateQueryData,
|
||||
).setIsSystemProviderRequired(passkeyOption.isSystemProviderRequired)
|
||||
.build()
|
||||
).build(),
|
||||
"com.google.android.youtube"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class CredentialSelectorActivity : ComponentActivity() {
|
||||
?: return Triple(false, false, null)
|
||||
if (viewModel != null && !viewModel.shouldCancelCurrentUi(cancelUiRequest.token)) {
|
||||
// Cancellation was for a different request, don't cancel the current UI.
|
||||
return Triple(false, false, null)
|
||||
return Triple(true, false, null)
|
||||
}
|
||||
val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationUi()
|
||||
Log.d(
|
||||
|
||||
@@ -69,7 +69,7 @@ class CredentialSelectorViewModel(
|
||||
|
||||
init{
|
||||
uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INIT,
|
||||
credManRepo.requestInfo.appPackageName)
|
||||
credManRepo.requestInfo?.appPackageName)
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
@@ -97,10 +97,10 @@ class CredentialSelectorViewModel(
|
||||
this.credManRepo = credManRepo
|
||||
uiState = credManRepo.initState()
|
||||
|
||||
if (this.credManRepo.requestInfo.token != credManRepo.requestInfo.token) {
|
||||
if (this.credManRepo.requestInfo?.token != credManRepo.requestInfo?.token) {
|
||||
this.uiMetrics.resetInstanceId()
|
||||
this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_NEW_REQUEST,
|
||||
credManRepo.requestInfo.appPackageName)
|
||||
credManRepo.requestInfo?.appPackageName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,14 +174,14 @@ class CredentialSelectorViewModel(
|
||||
private fun onInternalError() {
|
||||
Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
|
||||
this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INTERNAL_ERROR,
|
||||
credManRepo.requestInfo.appPackageName)
|
||||
credManRepo.requestInfo?.appPackageName)
|
||||
credManRepo.onParsingFailureCancel()
|
||||
uiState = uiState.copy(dialogState = DialogState.COMPLETE)
|
||||
}
|
||||
|
||||
/** Return true if the current UI's request token matches the UI cancellation request token. */
|
||||
fun shouldCancelCurrentUi(cancelRequestToken: IBinder): Boolean {
|
||||
return credManRepo.requestInfo.token.equals(cancelRequestToken)
|
||||
return credManRepo.requestInfo?.token?.equals(cancelRequestToken) ?: false
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
@@ -405,6 +405,6 @@ class CredentialSelectorViewModel(
|
||||
|
||||
@Composable
|
||||
fun logUiEvent(uiEventEnum: UiEventEnum) {
|
||||
this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo.appPackageName)
|
||||
this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.appPackageName)
|
||||
}
|
||||
}
|
||||
@@ -188,11 +188,11 @@ class GetFlowUtils {
|
||||
}
|
||||
|
||||
fun toRequestDisplayInfo(
|
||||
requestInfo: RequestInfo,
|
||||
requestInfo: RequestInfo?,
|
||||
context: Context,
|
||||
originName: String?,
|
||||
): com.android.credentialmanager.getflow.RequestDisplayInfo? {
|
||||
val getCredentialRequest = requestInfo.getCredentialRequest ?: return null
|
||||
val getCredentialRequest = requestInfo?.getCredentialRequest ?: return null
|
||||
val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any {
|
||||
val credentialOptionJetpack = CredentialOption.createFrom(
|
||||
it.type,
|
||||
@@ -450,10 +450,13 @@ class CreateFlowUtils {
|
||||
}
|
||||
|
||||
fun toRequestDisplayInfo(
|
||||
requestInfo: RequestInfo,
|
||||
requestInfo: RequestInfo?,
|
||||
context: Context,
|
||||
originName: String?,
|
||||
): RequestDisplayInfo? {
|
||||
if (requestInfo == null) {
|
||||
return null
|
||||
}
|
||||
val appLabel = originName
|
||||
?: getAppLabel(context.packageManager, requestInfo.appPackageName)
|
||||
?: return null
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.credentialmanager
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.slice.Slice
|
||||
import android.app.slice.SliceSpec
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
|
||||
import android.credentials.ui.AuthenticationEntry
|
||||
import android.credentials.ui.Entry
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.credentials.provider.BeginGetPasswordOption
|
||||
import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
|
||||
import androidx.credentials.provider.CreateEntry
|
||||
import androidx.credentials.provider.PasswordCredentialEntry
|
||||
import androidx.credentials.provider.PublicKeyCredentialEntry
|
||||
import androidx.credentials.provider.RemoteEntry
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
// TODO: remove once testing is complete
|
||||
class GetTestUtils {
|
||||
companion object {
|
||||
internal fun newAuthenticationEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
title: String,
|
||||
status: Int
|
||||
): AuthenticationEntry {
|
||||
val slice = Slice.Builder(
|
||||
Uri.EMPTY, SliceSpec("AuthenticationAction", 0)
|
||||
)
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
slice.addAction(
|
||||
pendingIntent,
|
||||
Slice.Builder(slice)
|
||||
.addHints(listOf("androidx.credentials.provider.authenticationAction" +
|
||||
".SLICE_HINT_PENDING_INTENT"))
|
||||
.build(),
|
||||
/*subType=*/null
|
||||
)
|
||||
slice.addText(
|
||||
title,
|
||||
null,
|
||||
listOf("androidx.credentials.provider.authenticationAction.SLICE_HINT_TITLE")
|
||||
)
|
||||
return AuthenticationEntry(
|
||||
key,
|
||||
subkey,
|
||||
slice.build(),
|
||||
status
|
||||
)
|
||||
}
|
||||
|
||||
internal fun newRemoteCredentialEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
): Entry {
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
return Entry(
|
||||
key,
|
||||
subkey,
|
||||
RemoteEntry.toSlice(RemoteEntry(pendingIntent))
|
||||
)
|
||||
}
|
||||
|
||||
internal fun newActionEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
text: String,
|
||||
subtext: String? = null,
|
||||
): Entry {
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("Action", 0))
|
||||
.addText(
|
||||
text, /*subType=*/null,
|
||||
listOf("androidx.credentials.provider.action.HINT_ACTION_TITLE")
|
||||
)
|
||||
.addText(
|
||||
subtext, /*subType=*/null,
|
||||
listOf("androidx.credentials.provider.action.HINT_ACTION_SUBTEXT")
|
||||
)
|
||||
sliceBuilder.addAction(
|
||||
pendingIntent,
|
||||
Slice.Builder(sliceBuilder)
|
||||
.addHints(
|
||||
listOf("androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT")
|
||||
)
|
||||
.build(),
|
||||
/*subType=*/null
|
||||
)
|
||||
return Entry(
|
||||
key,
|
||||
subkey,
|
||||
sliceBuilder.build()
|
||||
)
|
||||
}
|
||||
|
||||
internal fun newPasswordEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
userName: String,
|
||||
userDisplayName: String?,
|
||||
lastUsedTime: Instant?,
|
||||
): Entry {
|
||||
val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
|
||||
.setPackage("com.androidauth.androidvault")
|
||||
intent.putExtra("provider_extra_sample", "testprovider")
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 1,
|
||||
intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
or PendingIntent.FLAG_ONE_SHOT)
|
||||
)
|
||||
val passwordEntry = PasswordCredentialEntry.Builder(
|
||||
context, userName, pendingIntent, BeginGetPasswordOption(Bundle(), "id"))
|
||||
.setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
|
||||
return Entry(key, subkey, passwordEntry.slice, Intent())
|
||||
}
|
||||
|
||||
internal fun newPasskeyEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
userName: String,
|
||||
userDisplayName: String?,
|
||||
lastUsedTime: Instant?,
|
||||
isAutoSelectAllowed: Boolean = false,
|
||||
): Entry {
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
val candidateQueryData = Bundle()
|
||||
candidateQueryData.putBoolean(
|
||||
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED",
|
||||
isAutoSelectAllowed
|
||||
)
|
||||
val passkeyEntry = PublicKeyCredentialEntry.Builder(
|
||||
context,
|
||||
userName,
|
||||
pendingIntent,
|
||||
BeginGetPublicKeyCredentialOption(candidateQueryData, "id", "requestjson")
|
||||
).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime)
|
||||
.setAutoSelectAllowed(isAutoSelectAllowed).build()
|
||||
return Entry(key, subkey, passkeyEntry.slice, Intent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CreateTestUtils {
|
||||
companion object {
|
||||
private const val TYPE_TOTAL_CREDENTIAL = "TOTAL_CREDENTIAL_COUNT_TYPE"
|
||||
private const val SLICE_HINT_ACCOUNT_NAME =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
|
||||
private const val SLICE_HINT_NOTE =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_NOTE"
|
||||
private const val SLICE_HINT_ICON =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
|
||||
private const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
|
||||
private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
|
||||
private const val SLICE_HINT_PENDING_INTENT =
|
||||
"androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
|
||||
|
||||
internal fun newCreateEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
providerUserDisplayName: String,
|
||||
passwordCount: Int?,
|
||||
passkeyCount: Int?,
|
||||
totalCredentialCount: Int?,
|
||||
lastUsedTime: Instant?,
|
||||
footerDescription: String?,
|
||||
): Entry {
|
||||
val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
|
||||
.setPackage("com.androidauth.androidvault")
|
||||
intent.putExtra("provider_extra_sample", "testprovider")
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 1,
|
||||
intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
or PendingIntent.FLAG_ONE_SHOT)
|
||||
)
|
||||
val credCountMap = mutableMapOf<String, Int>()
|
||||
passwordCount?.let { credCountMap.put(TYPE_PASSWORD_CREDENTIAL, it) }
|
||||
passkeyCount?.let {
|
||||
credCountMap.put("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", it)
|
||||
}
|
||||
totalCredentialCount?.let { credCountMap.put(TYPE_TOTAL_CREDENTIAL, it) }
|
||||
return Entry(
|
||||
key,
|
||||
subkey,
|
||||
CreateEntry.toSlice(
|
||||
CreateEntry(
|
||||
accountName = providerUserDisplayName,
|
||||
pendingIntent = pendingIntent,
|
||||
description = footerDescription,
|
||||
lastUsedTime = lastUsedTime,
|
||||
icon = null,
|
||||
passwordCredentialCount = passwordCount,
|
||||
publicKeyCredentialCount = passkeyCount,
|
||||
totalCredentialCount = totalCredentialCount,
|
||||
)
|
||||
),
|
||||
Intent()
|
||||
)
|
||||
}
|
||||
|
||||
internal fun newRemoteCreateEntry(
|
||||
context: Context,
|
||||
key: String,
|
||||
subkey: String,
|
||||
): Entry {
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
return Entry(
|
||||
key,
|
||||
subkey,
|
||||
RemoteEntry.toSlice(RemoteEntry(pendingIntent))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class UIMetrics() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun log(event: UiEventLogger.UiEventEnum, packageName: String) {
|
||||
fun log(event: UiEventLogger.UiEventEnum, packageName: String?) {
|
||||
val instanceId: InstanceId = mInstanceId
|
||||
LaunchedEffect(true) {
|
||||
mUiEventLogger.logWithInstanceId(event, /*uid=*/0, packageName, instanceId)
|
||||
@@ -51,13 +51,13 @@ class UIMetrics() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun log(event: UiEventLogger.UiEventEnum, instanceId: InstanceId, packageName: String) {
|
||||
fun log(event: UiEventLogger.UiEventEnum, instanceId: InstanceId, packageName: String?) {
|
||||
LaunchedEffect(true) {
|
||||
mUiEventLogger.logWithInstanceId(event, /*uid=*/0, packageName, instanceId)
|
||||
}
|
||||
}
|
||||
|
||||
fun logNormal(event: UiEventLogger.UiEventEnum, packageName: String) {
|
||||
fun logNormal(event: UiEventLogger.UiEventEnum, packageName: String?) {
|
||||
mUiEventLogger.logWithInstanceId(event, /*uid=*/0, packageName, mInstanceId)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user