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

Commit 7e83f824 authored by Reema Bajwa's avatar Reema Bajwa Committed by Android (Google) Code Review
Browse files

Merge "Add ids to credential entries"

parents 43467076 ba138f61
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -40258,7 +40258,7 @@ package android.service.credentials {
  }
  public class BeginGetCredentialOption implements android.os.Parcelable {
    ctor public BeginGetCredentialOption(@NonNull String, @NonNull android.os.Bundle);
    ctor public BeginGetCredentialOption(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
    method public int describeContents();
    method @NonNull public android.os.Bundle getCandidateQueryData();
    method @NonNull public String getType();
@@ -40341,8 +40341,9 @@ package android.service.credentials {
  }
  public class CredentialEntry implements android.os.Parcelable {
    ctor public CredentialEntry(@NonNull String, @NonNull android.app.slice.Slice);
    ctor public CredentialEntry(@NonNull android.service.credentials.BeginGetCredentialOption, @NonNull android.app.slice.Slice);
    method public int describeContents();
    method @NonNull public android.service.credentials.BeginGetCredentialOption getBeginGetCredentialOption();
    method @NonNull public android.app.slice.Slice getSlice();
    method @NonNull public String getType();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
+25 −3
Original line number Diff line number Diff line
@@ -39,6 +39,11 @@ import com.android.internal.util.Preconditions;
 */
@SuppressLint("ParcelNotFinal")
public class BeginGetCredentialOption implements Parcelable {
    /**
     * A unique id associated with this request option.
     */
    @NonNull
    private final String mId;

    /**
     * The requested credential type.
@@ -52,6 +57,18 @@ public class BeginGetCredentialOption implements Parcelable {
    @NonNull
    private final Bundle mCandidateQueryData;

    /**
     * Returns the unique id associated with this request. Providers must pass this id
     * to the constructor of {@link CredentialEntry} while creating a candidate credential
     * entry for this request option.
     *
     * @hide
     */
    @NonNull
    public String getId() {
        return mId;
    }

    /**
     * Returns the requested credential type.
     */
@@ -80,6 +97,7 @@ public class BeginGetCredentialOption implements Parcelable {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString8(mType);
        dest.writeBundle(mCandidateQueryData);
        dest.writeString8(mId);
    }

    @Override
@@ -92,20 +110,22 @@ public class BeginGetCredentialOption implements Parcelable {
        return "GetCredentialOption {"
                + "type=" + mType
                + ", candidateQueryData=" + mCandidateQueryData
                + ", id=" + mId
                + "}";
    }

    /**
     * Constructs a {@link BeginGetCredentialOption}.
     *
     * @param id the unique id associated with this option
     * @param type               the requested credential type
     * @param candidateQueryData the request candidateQueryData
     *
     * @throws IllegalArgumentException If type is empty.
     */
    public BeginGetCredentialOption(
            @NonNull String type,
            @NonNull String id, @NonNull String type,
            @NonNull Bundle candidateQueryData) {
        mId = id;
        mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
        mCandidateQueryData = requireNonNull(
                candidateQueryData, "candidateQueryData must not be null");
@@ -114,11 +134,13 @@ public class BeginGetCredentialOption implements Parcelable {
    private BeginGetCredentialOption(@NonNull Parcel in) {
        String type = in.readString8();
        Bundle candidateQueryData = in.readBundle();
        String id = in.readString8();

        mType = type;
        AnnotationValidations.validate(NonNull.class, null, mType);
        mCandidateQueryData = candidateQueryData;
        AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
        mId = id;
    }

    public static final @NonNull Creator<BeginGetCredentialOption> CREATOR =
+64 −5
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package android.service.credentials;

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.app.slice.Slice;
@@ -29,9 +32,11 @@ import android.os.Parcelable;
 * user.
 *
 * <p>If user selects this entry, the corresponding {@link PendingIntent},
 * set on the {@code slice} as a {@link androidx.slice.core.SliceAction} will be
 * invoked to launch activities that require some user engagement before getting
 * the credential corresponding to this entry, e.g. authentication, confirmation etc.
 * set on the {@code slice} will be invoked to launch activities that require some user engagement
 * before getting the credential corresponding to this entry, e.g. authentication,
 * confirmation etc. The extras associated with the resulting {@link android.app.Activity} will
 * also contain the complete credential request containing all required parameters. This request
 * can be retrieved against {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_REQUEST}.
 *
 * Once the activity fulfills the required user engagement, the {@link android.app.Activity}
 * result should be set to {@link android.app.Activity#RESULT_OK}, and the
@@ -42,24 +47,69 @@ import android.os.Parcelable;
 * object passed into the constructor. Any other field will not be parceled through. If the
 * derived class has custom parceling implementation, this class will not be able to unpack
 * the parcel without having access to that implementation.
 *
 * <p>While creating this entry, providers must set a {@code requestId} to be retrieved
 * from {@link BeginGetCredentialOption#getId()}, to determine for which request this entry is
 * being presented to the user. This will ensure that when user selects the entry, the correct
 * complete request is added to the {@link PendingIntent} mentioned above.
 */
@SuppressLint("ParcelNotFinal")
public class CredentialEntry implements Parcelable {
    /** The request option that corresponds to this entry. **/
    private final @Nullable BeginGetCredentialOption mBeginGetCredentialOption;

    /** The type of the credential entry to be shown on the UI. */
    private final @NonNull String mType;


    /** The object containing display content to be shown along with this credential entry
     * on the UI. */
    private final @NonNull Slice mSlice;

    /**
     * Creates an entry that is associated with a {@link BeginGetCredentialOption} request.
     * Providers must use this constructor when they extend from {@link CredentialProviderService}
     * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
     * credential retrieval requests.
     *
     * @param beginGetCredentialOption the request option for which this credential entry is
     *                                 being constructed This helps maintain an association,
     *                                 such that when the user selects this entry, providers
     *                                 can receive the conmplete corresponding request.
     * @param slice the slice containing the metadata to be shown on the UI. Must be
     *              constructed through the androidx.credentials jetpack library.
     */
    public CredentialEntry(@NonNull BeginGetCredentialOption beginGetCredentialOption,
            @NonNull Slice slice) {
        mBeginGetCredentialOption = requireNonNull(beginGetCredentialOption,
                "beginGetCredentialOption must not be null");
        mType = requireNonNull(mBeginGetCredentialOption.getType(),
                "type must not be null");
        mSlice = requireNonNull(slice, "slice must not be null");
    }

    /**
     * Creates an entry that is independent of an incoming {@link BeginGetCredentialOption}
     * request. Providers must use this constructor for constructing entries to be registered
     * with the framework outside of the span of an API call.
     *
     * @param type the type of the credential
     * @param slice the slice containing the metadata to be shown on the UI. Must be
     *              constructed through the androidx.credentials jetpack library.
     *
     * @hide
     */
    // TODO: Unhide this constructor when the registry APIs are stable
    public CredentialEntry(@NonNull String type, @NonNull Slice slice) {
        mType = type;
        mSlice = slice;
        mBeginGetCredentialOption = null;
        mType = requireNonNull(type, "type must not be null");
        mSlice = requireNonNull(slice, "slice must not be null");
    }

    private CredentialEntry(@NonNull Parcel in) {
        mType = in.readString8();
        mSlice = in.readTypedObject(Slice.CREATOR);
        mBeginGetCredentialOption = in.readTypedObject(BeginGetCredentialOption.CREATOR);
    }

    @NonNull
@@ -85,6 +135,15 @@ public class CredentialEntry implements Parcelable {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString8(mType);
        dest.writeTypedObject(mSlice, flags);
        dest.writeTypedObject(mBeginGetCredentialOption, flags);
    }

    /**
     * Returns the request option for which this credential entry has been constructed.
     */
    @NonNull
    public BeginGetCredentialOption getBeginGetCredentialOption() {
        return mBeginGetCredentialOption;
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -229,7 +229,7 @@ public final class ProviderCreateSession extends ProviderSession<

        // Populate the save entries
        for (CreateEntry createEntry : saveEntries) {
            String entryId = generateEntryId();
            String entryId = generateUniqueId();
            mUiSaveEntries.put(entryId, createEntry);
            Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
            uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, createEntry.getSlice(),
+35 −26
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Central provider session that listens for provider callbacks, and maintains provider state.
@@ -67,6 +66,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
    // Key to be used as an entry key for a credential entry
    private static final String CREDENTIAL_ENTRY_KEY = "credential_key";

    @NonNull
    private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
    @NonNull
    private final Map<String, CredentialEntry> mUiCredentialEntries = new HashMap<>();
    @NonNull
@@ -91,12 +92,16 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
                filterOptions(providerInfo.getCapabilities(),
                        getRequestSession.mClientRequest);
        if (filteredRequest != null) {
            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
                    new HashMap<>();
            BeginGetCredentialRequest beginGetCredentialRequest = constructQueryPhaseRequest(
                    filteredRequest, getRequestSession.mClientAppInfo,
                    getRequestSession.mClientRequest.alwaysSendAppInfoToProvider());
                    getRequestSession.mClientRequest.alwaysSendAppInfoToProvider(),
                    beginGetOptionToCredentialOptionMap);
            return new ProviderGetSession(context, providerInfo, getRequestSession, userId,
                    remoteCredentialService, beginGetCredentialRequest, filteredRequest,
                    getRequestSession.mClientAppInfo);
                    getRequestSession.mClientAppInfo,
                    beginGetOptionToCredentialOptionMap);
        }
        Log.i(TAG, "Unable to create provider session");
        return null;
@@ -105,15 +110,18 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
    private static BeginGetCredentialRequest constructQueryPhaseRequest(
            android.credentials.GetCredentialRequest filteredRequest,
            CallingAppInfo callingAppInfo,
            boolean propagateToProvider) {
            boolean propagateToProvider,
            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap
    ) {
        BeginGetCredentialRequest.Builder builder = new BeginGetCredentialRequest.Builder();
        builder.setBeginGetCredentialOptions(
                filteredRequest.getCredentialOptions().stream().map(
                        option -> {
                            return new BeginGetCredentialOption(
                                    option.getType(),
                                    option.getCandidateQueryData());
                        }).collect(Collectors.toList()));
        filteredRequest.getCredentialOptions().forEach(option -> {
            String id = generateUniqueId();
            builder.addBeginGetCredentialOption(
                    new BeginGetCredentialOption(
                            id, option.getType(), option.getCandidateQueryData())
            );
            beginGetOptionToCredentialOptionMap.put(id, option);
        });
        if (propagateToProvider) {
            builder.setCallingAppInfo(callingAppInfo);
        }
@@ -152,11 +160,13 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
            int userId, RemoteCredentialService remoteCredentialService,
            BeginGetCredentialRequest beginGetRequest,
            android.credentials.GetCredentialRequest completeGetRequest,
            CallingAppInfo callingAppInfo) {
            CallingAppInfo callingAppInfo,
            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap) {
        super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
        mCompleteRequest = completeGetRequest;
        mCallingAppInfo = callingAppInfo;
        setStatus(Status.PENDING);
        mBeginGetOptionToCredentialOptionMap = beginGetOptionToCredentialOptionMap;
    }

    /** Called when the provider response has been updated by an external source. */
@@ -260,7 +270,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        if (remoteCredentialEntry == null) {
            return null;
        }
        String entryId = generateEntryId();
        String entryId = generateUniqueId();
        Entry remoteEntry = new Entry(REMOTE_ENTRY_KEY, entryId, remoteCredentialEntry.getSlice());
        mUiRemoteEntry = new Pair<>(entryId, remoteCredentialEntry);
        return remoteEntry;
@@ -271,7 +281,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        List<Entry> authenticationUiEntries = new ArrayList<>();

        for (Action authenticationAction : authenticationEntries) {
            String entryId = generateEntryId();
            String entryId = generateUniqueId();
            mUiAuthenticationEntries.put(entryId, authenticationAction);
            authenticationUiEntries.add(new Entry(
                    AUTHENTICATION_ACTION_ENTRY_KEY, entryId,
@@ -288,7 +298,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential

        // Populate the credential entries
        for (CredentialEntry credentialEntry : credentialEntries) {
            String entryId = generateEntryId();
            String entryId = generateUniqueId();
            mUiCredentialEntries.put(entryId, credentialEntry);
            Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
            credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
@@ -298,18 +308,17 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        return credentialUiEntries;
    }

    private Intent setUpFillInIntent(String type) {
        Intent intent = new Intent();
        for (CredentialOption option : mCompleteRequest.getCredentialOptions()) {
            if (option.getType().equals(type)) {
                intent.putExtra(
                        CredentialProviderService
                                .EXTRA_GET_CREDENTIAL_REQUEST,
                        new GetCredentialRequest(mCallingAppInfo, option));
                return intent;
    private Intent setUpFillInIntent(@Nullable String id) {
        // TODO: Determine if we should skip this entry if entry id is not set, or is set
        // 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 (id == null || mBeginGetOptionToCredentialOptionMap.get(id) == null) {
            Log.i(TAG, "Id from Credential Entry does not resolve to a valid option");
        }
        }
        return intent;
        return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
                new GetCredentialRequest(
                        mCallingAppInfo, mBeginGetOptionToCredentialOptionMap.get(id)));
    }

    private Intent setUpFillInIntentForAuthentication() {
Loading