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

Commit da63e412 authored by Daniel's avatar Daniel Committed by Daniel Kim
Browse files

Associate autofillId with credential request and response

Currently, we bundle credential options from multiple views into a
single credential request. During the bundling, the information of
which view the option originates from gets lost. To retain the view
association for autofill purposes, pass the autofillId to credential
option when requesting to credman and pass the autofillId again
when the credential entry gets returned from credman.

Bug: 299344479
Test: Built locally
Change-Id: I2cae247c3aff274afc47646bfa72483c8c6c053c
parent 1411481b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -53,6 +53,15 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
        mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
    }

    /**
     * Returns candidate provider data list.
     *
     * @hide
     */
    public List<GetCredentialProviderData> getCandidateProviderDataList() {
        return mCandidateProviderDataList;
    }

    protected GetCandidateCredentialsResponse(Parcel in) {
        List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
        in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
+12 −0
Original line number Diff line number Diff line
@@ -153,6 +153,18 @@ public abstract class CredentialProviderService extends Service {
    public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST =
            "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST";

    /**
     * The key to autofillId associated with the requested credential option and the corresponding
     * credential entry. The associated autofillId will be contained inside the candidate query
     * bundle of {@link android.credentials.CredentialOption} if requested through the
     * {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting
     * credential entry will  contain the autofillId inside its framework extras intent.
     *
     * @hide
     */
    public static final String EXTRA_AUTOFILL_ID =
            "android.service.credentials.extra.AUTOFILL_ID";

    private static final String TAG = "CredProviderService";

     /**
+29 −18
Original line number Diff line number Diff line
@@ -18,21 +18,23 @@ package com.android.credentialmanager.autofill

import android.app.assist.AssistStructure
import android.content.Context
import android.credentials.GetCredentialRequest
import android.credentials.CredentialManager
import android.credentials.GetCandidateCredentialsResponse
import android.credentials.CredentialOption
import android.credentials.GetCandidateCredentialsException
import android.credentials.GetCandidateCredentialsResponse
import android.credentials.GetCredentialRequest
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.service.autofill.FillRequest
import android.service.autofill.AutofillService
import android.service.autofill.FillResponse
import android.service.autofill.FillCallback
import android.service.autofill.SaveRequest
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
import android.service.autofill.SaveCallback
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
import org.json.JSONObject
import java.util.concurrent.Executors

@@ -129,27 +131,31 @@ class CredentialAutofillService : AutofillService() {
    }

    private fun traverseNode(
            viewNode: AssistStructure.ViewNode?,
            viewNode: AssistStructure.ViewNode,
            cmRequests: MutableList<CredentialOption>
    ) {
        val options = getCredentialOptionsFromViewNode(viewNode)
        viewNode.autofillId?.let {
            val options = getCredentialOptionsFromViewNode(viewNode, it)
            cmRequests.addAll(options)
        }

        val children: List<AssistStructure.ViewNode>? =
                viewNode?.run {
        val children: List<AssistStructure.ViewNode> =
                viewNode.run {
                    (0 until childCount).map { getChildAt(it) }
                }

        children?.forEach { childNode: AssistStructure.ViewNode ->
        children.forEach { childNode: AssistStructure.ViewNode ->
            traverseNode(childNode, cmRequests)
        }
    }

    private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
            List<CredentialOption> {
    private fun getCredentialOptionsFromViewNode(
            viewNode: AssistStructure.ViewNode,
            autofillId: AutofillId
    ): List<CredentialOption> {
        // TODO(b/293945193) Replace with isCredential check from viewNode
        val credentialHints: MutableList<String> = mutableListOf()
        if (viewNode != null && viewNode.autofillHints != null) {
        if (viewNode.autofillHints != null) {
            for (hint in viewNode.autofillHints!!) {
                if (hint.startsWith(CRED_HINT_PREFIX)) {
                    credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
@@ -159,12 +165,14 @@ class CredentialAutofillService : AutofillService() {

        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
        for (credentialHint in credentialHints) {
            convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
            convertJsonToCredentialOption(credentialHint, autofillId)
                    .let { credentialOptions.addAll(it) }
        }
        return credentialOptions
    }

    private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
    private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId):
            List<CredentialOption> {
        // TODO(b/302000646) Move this logic to jetpack so that is consistent
        //  with building the json
        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
@@ -173,11 +181,14 @@ class CredentialAutofillService : AutofillService() {
        val options = json.getJSONArray(CRED_OPTIONS_KEY)
        for (i in 0 until options.length()) {
            val option = options.getJSONObject(i)

            val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
            candidateBundle.putParcelable(
                    CredentialProviderService.EXTRA_AUTOFILL_ID,
                    autofillId)
            credentialOptions.add(CredentialOption(
                    option.getString(TYPE_KEY),
                    convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
                    convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
                    candidateBundle,
                    option.getBoolean(SYS_PROVIDER_REQ_KEY),
            ))
        }
+16 −4
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.credentials.ui.Entry;
import android.credentials.ui.GetCredentialProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.os.ICancellationSignal;
import android.service.autofill.Flags;
import android.service.credentials.Action;
import android.service.credentials.BeginGetCredentialOption;
import android.service.credentials.BeginGetCredentialRequest;
@@ -42,6 +43,7 @@ import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
import android.util.Pair;
import android.util.Slog;
import android.view.autofill.AutofillId;

import java.util.ArrayList;
import java.util.HashMap;
@@ -379,13 +381,23 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        // but does not resolve to a valid option. For now, not skipping it because
        // it may be possible that the provider adds their own extras and expects to receive
        // those and complete the flow.
        if (mBeginGetOptionToCredentialOptionMap.get(id) == null) {
        Intent intent = new Intent();
        CredentialOption credentialOption = mBeginGetOptionToCredentialOptionMap.get(id);
        if (credentialOption == null) {
            Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option");
            return new Intent();
            return intent;
        }
        return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
        AutofillId autofillId = credentialOption
                .getCandidateQueryData()
                .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
        if (autofillId != null && Flags.autofillCredmanIntegration()) {
            intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
        }
        return intent.putExtra(
                CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
                new GetCredentialRequest(
                        mCallingAppInfo, List.of(mBeginGetOptionToCredentialOptionMap.get(id))));
                        mCallingAppInfo,
                        List.of(credentialOption)));
    }

    private Intent setUpFillInIntentWithQueryRequest() {