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

Commit 4c114067 authored by Helen Qin's avatar Helen Qin Committed by Android (Google) Code Review
Browse files

Merge "Remove test data fallback from the UI app." into udc-dev

parents bbbe3c79 4c0db23c
Loading
Loading
Loading
Loading
+29 −268
Original line number Diff line number Diff line
@@ -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"
        )
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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(
+6 −6
Original line number Diff line number Diff line
@@ -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)
    }
}
 No newline at end of file
+6 −3
Original line number Diff line number Diff line
@@ -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
+0 −252

File deleted.

Preview size limit exceeded, changes collapsed.

Loading