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

Commit 221739a8 authored by Reema Bajwa's avatar Reema Bajwa Committed by Automerger Merge Worker
Browse files

Merge "Allow provider filter to be used for query phase" into udc-dev am: 63067386

parents 1cbae357 63067386
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ package android {
    field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE";
    field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
    field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
    field public static final String CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS = "android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS";
    field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN";
    field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
    field public static final String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
@@ -13672,8 +13673,9 @@ package android.credentials {
  }
  public final class CredentialOption implements android.os.Parcelable {
    ctor public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
    ctor @Deprecated public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
    method public int describeContents();
    method @NonNull public java.util.Set<android.content.ComponentName> getAllowedProviders();
    method @NonNull public android.os.Bundle getCandidateQueryData();
    method @NonNull public android.os.Bundle getCredentialRetrievalData();
    method @NonNull public String getType();
@@ -13683,6 +13685,14 @@ package android.credentials {
    field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
  }
  public static final class CredentialOption.Builder {
    ctor public CredentialOption.Builder(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle);
    method @NonNull public android.credentials.CredentialOption.Builder addAllowedProvider(@NonNull android.content.ComponentName);
    method @NonNull public android.credentials.CredentialOption build();
    method @NonNull public android.credentials.CredentialOption.Builder setAllowedProviders(@NonNull java.util.Set<android.content.ComponentName>);
    method @NonNull public android.credentials.CredentialOption.Builder setIsSystemProviderRequired(boolean);
  }
  public class GetCredentialException extends java.lang.Exception {
    ctor public GetCredentialException(@NonNull String, @Nullable String);
    ctor public GetCredentialException(@NonNull String, @Nullable String, @Nullable Throwable);
+169 −2
Original line number Diff line number Diff line
@@ -16,16 +16,25 @@

package android.credentials;

import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;

import androidx.annotation.RequiresPermission;

import com.android.internal.util.AnnotationValidations;
import com.android.internal.util.Preconditions;

import java.util.Set;

/**
 * Information about a specific type of credential to be requested during a {@link
 * CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor,
@@ -65,6 +74,14 @@ public final class CredentialOption implements Parcelable {
     */
    private final boolean mIsSystemProviderRequired;

    /**
     * A list of {@link ComponentName}s corresponding to the providers that this option must be
     * queried against.
     */
    @NonNull
    private final ArraySet<ComponentName> mAllowedProviders;


    /**
     * Returns the requested credential type.
     */
@@ -105,12 +122,22 @@ public final class CredentialOption implements Parcelable {
        return mIsSystemProviderRequired;
    }

    /**
     * Returns the set of {@link ComponentName} corresponding to providers that must receive
     * this option.
     */
    @NonNull
    public Set<ComponentName> getAllowedProviders() {
        return mAllowedProviders;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString8(mType);
        dest.writeBundle(mCredentialRetrievalData);
        dest.writeBundle(mCandidateQueryData);
        dest.writeBoolean(mIsSystemProviderRequired);
        dest.writeArraySet(mAllowedProviders);
    }

    @Override
@@ -125,6 +152,7 @@ public final class CredentialOption implements Parcelable {
                + ", requestData=" + mCredentialRetrievalData
                + ", candidateQueryData=" + mCandidateQueryData
                + ", isSystemProviderRequired=" + mIsSystemProviderRequired
                + ", allowedProviders=" + mAllowedProviders
                + "}";
    }

@@ -139,17 +167,50 @@ public final class CredentialOption implements Parcelable {
     *                                 provider
     * @throws IllegalArgumentException If type is empty.
     */
    public CredentialOption(
    private CredentialOption(
            @NonNull String type,
            @NonNull Bundle credentialRetrievalData,
            @NonNull Bundle candidateQueryData,
            boolean isSystemProviderRequired) {
            boolean isSystemProviderRequired,
            @NonNull ArraySet<ComponentName> allowedProviders) {
        mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
        mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
                "requestData must not be null");
        mCandidateQueryData = requireNonNull(candidateQueryData,
                "candidateQueryData must not be null");
        mIsSystemProviderRequired = isSystemProviderRequired;
        mAllowedProviders = requireNonNull(allowedProviders, "providerFilterSer must"
                + "not be empty");
    }

    /**
     * Constructs a {@link CredentialOption}.
     *
     * @param type                     the requested credential type
     * @param credentialRetrievalData  the request data
     * @param candidateQueryData       the partial request data that will be sent to the provider
     *                                 during the initial credential candidate query stage
     * @param isSystemProviderRequired whether the request must only be fulfilled by a system
     *                                 provider
     * @throws IllegalArgumentException If type is empty, or null.
     * @throws NullPointerException If {@code credentialRetrievalData}, or
     * {@code candidateQueryData} is null.
     *
     * @deprecated replaced by Builder
     */
    @Deprecated
    public CredentialOption(
            @NonNull String type,
            @NonNull Bundle credentialRetrievalData,
            @NonNull Bundle candidateQueryData,
            boolean isSystemProviderRequired) {
        this(
                type,
                credentialRetrievalData,
                candidateQueryData,
                isSystemProviderRequired,
                new ArraySet<>()
        );
    }

    private CredentialOption(@NonNull Parcel in) {
@@ -165,6 +226,8 @@ public final class CredentialOption implements Parcelable {
        mCandidateQueryData = candidateQueryData;
        AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
        mIsSystemProviderRequired = isSystemProviderRequired;
        mAllowedProviders = (ArraySet<ComponentName>) in.readArraySet(null);
        AnnotationValidations.validate(NonNull.class, null, mAllowedProviders);
    }

    @NonNull
@@ -179,4 +242,108 @@ public final class CredentialOption implements Parcelable {
            return new CredentialOption(in);
        }
    };

    /** A builder for {@link CredentialOption}. */
    public static final class Builder {

        @NonNull
        private String mType;

        @NonNull
        private Bundle mCredentialRetrievalData;

        @NonNull
        private Bundle mCandidateQueryData;

        private boolean mIsSystemProviderRequired = false;

        @NonNull
        private ArraySet<ComponentName> mAllowedProviders = new ArraySet<>();

        /**
         * @param type                    the type of the credential option
         * @param credentialRetrievalData the full request data
         * @param candidateQueryData      the partial request data that will be sent to the provider
         *                                during the initial credential candidate query stage.
         * @throws IllegalArgumentException If {@code type} is null, or empty
         * @throws NullPointerException     If {@code credentialRetrievalData}, or
         *                                  {@code candidateQueryData} is null
         */
        public Builder(@NonNull String type, @NonNull Bundle credentialRetrievalData,
                @NonNull Bundle candidateQueryData) {
            mType = Preconditions.checkStringNotEmpty(type, "type must not be "
                    + "null, or empty");
            mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
                    "credentialRetrievalData must not be null");
            mCandidateQueryData = requireNonNull(candidateQueryData,
                    "candidateQueryData must not be null");
        }

        /**
         * Sets a true/false value corresponding to whether this option must be serviced by
         * system credentials providers only.
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        @NonNull
        public Builder setIsSystemProviderRequired(boolean isSystemProviderRequired) {
            mIsSystemProviderRequired = isSystemProviderRequired;
            return this;
        }

        /**
         * Adds a provider {@link ComponentName} to be queried while gathering credentials from
         * credential providers on the device.
         *
         * If no candidate providers are specified, all user configured and system credential
         * providers will be queried in the candidate query phase.
         *
         * If an invalid component name is provided, or a service corresponding to the
         * component name does not exist on the device, that component name is ignored.
         * If all component names are invalid, or not present on the device, no providers
         * are queried and no credentials are retrieved.
         *
         * @throws NullPointerException If {@code allowedProvider} is null
         */
        @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
        @NonNull
        public Builder addAllowedProvider(@NonNull ComponentName allowedProvider) {
            mAllowedProviders.add(requireNonNull(allowedProvider,
                    "allowedProvider must not be null"));
            return this;
        }

        /**
         * Sets a set of provider {@link ComponentName} to be queried while gathering credentials
         * from credential providers on the device.
         *
         * If no candidate providers are specified, all user configured and system credential
         * providers will be queried in the candidate query phase.
         *
         * If an invalid component name is provided, or a service corresponding to the
         * component name does not exist on the device, that component name is ignored.
         * If all component names are invalid, or not present on the device, no providers
         * are queried and no credentials are retrieved.
         *
         * @throws NullPointerException If {@code allowedProviders} is null, or any of its
         * elements are null.
         */
        @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
        @NonNull
        public Builder setAllowedProviders(@NonNull Set<ComponentName> allowedProviders) {
            Preconditions.checkCollectionElementsNotNull(
                    allowedProviders,
                    /*valueName=*/ "allowedProviders");
            mAllowedProviders = new ArraySet<>(allowedProviders);
            return this;
        }

        /**
         * Builds a {@link CredentialOption}.
         */
        @NonNull
        public CredentialOption build() {
            return new CredentialOption(mType, mCredentialRetrievalData, mCandidateQueryData,
                    mIsSystemProviderRequired, mAllowedProviders);
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -4482,6 +4482,12 @@
    <permission android:name="android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"
                android:protectionLevel="signature|privileged" />

    <!-- Allows specifying candidate credential providers to be queried in Credential Manager
    get flows, or to be preferred as a default in the Credential Manager create flows.
     <p>Protection level: normal -->
    <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"
                android:protectionLevel="normal" />

    <!-- Allows a browser to invoke credential manager APIs on behalf of another RP.
        <p>Protection level: normal -->
    <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"
+6 −6
Original line number Diff line number Diff line
@@ -467,19 +467,19 @@ class CredentialManagerRepo(
            GetCredentialRequest.Builder(
                Bundle()
            ).addCredentialOption(
                CredentialOption(
                CredentialOption.Builder(
                    passwordOption.type,
                    passwordOption.requestData,
                    passwordOption.candidateQueryData,
                    passwordOption.isSystemProviderRequired
                )
                ).setIsSystemProviderRequired(passwordOption.isSystemProviderRequired)
                .build()
            ).addCredentialOption(
                CredentialOption(
                CredentialOption.Builder(
                    passkeyOption.type,
                    passkeyOption.requestData,
                    passkeyOption.candidateQueryData,
                    passkeyOption.isSystemProviderRequired
                )
                ).setIsSystemProviderRequired(passkeyOption.isSystemProviderRequired)
                .build()
            ).build(),
            "com.google.android.youtube"
        )
+18 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.credentials;

import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static android.Manifest.permission.LAUNCH_CREDENTIAL_SELECTOR;
import static android.content.Context.CREDENTIAL_SERVICE;
@@ -124,7 +125,8 @@ public final class CredentialManagerService
        serviceInfos.forEach(
                info -> {
                    services.add(
                            new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
                            new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
                                    info));
                });
        return services;
    }
@@ -418,6 +420,7 @@ public final class CredentialManagerService
                // Check privileged permissions
                mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
            }
            enforcePermissionForAllowedProviders(request);

            final int userId = UserHandle.getCallingUserId();
            final int callingUid = Binder.getCallingUid();
@@ -447,6 +450,9 @@ public final class CredentialManagerService
            // TODO(b/273308895): implement

            ICancellationSignal cancelTransport = CancellationSignal.createTransport();

            enforcePermissionForAllowedProviders(request);

            return cancelTransport;
        }

@@ -823,6 +829,17 @@ public final class CredentialManagerService
        }
    }

    private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
        boolean containsAllowedProviders = request.getCredentialOptions()
                .stream()
                .anyMatch(option -> option.getAllowedProviders() != null
                        && !option.getAllowedProviders().isEmpty());
        if (containsAllowedProviders) {
            mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
                    null);
        }
    }

    private void enforceCallingPackage(String callingPackage, int callingUid) {
        int packageUid;
        PackageManager pm = mContext.createContextAsUser(
Loading