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

Commit 6939babc authored by Omer Ozer's avatar Omer Ozer
Browse files

Changing flat request string to a set of element keys.

Bug: 274667622
Test: local
Change-Id: I719483380d86f12b17fec2d1ca58bb1b8ac56a64
parent a38c6d8c
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -13645,10 +13645,10 @@ package android.credentials {
  }
  public final class CredentialDescription implements android.os.Parcelable {
    ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
    ctor public CredentialDescription(@NonNull String, @NonNull java.util.Set<java.lang.String>, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
    method public int describeContents();
    method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
    method @NonNull public String getFlattenedRequestString();
    method @NonNull public java.util.Set<java.lang.String> getSupportedElementKeys();
    method @NonNull public String getType();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
@@ -13675,7 +13675,7 @@ package android.credentials {
    method public boolean isSystemProviderRequired();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialOption> CREATOR;
    field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
    field public static final String SUPPORTED_ELEMENT_KEYS = "android.credentials.GetCredentialOption.SUPPORTED_ELEMENT_KEYS";
  }
  public static final class CredentialOption.Builder {
+17 −16
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ import com.android.internal.util.AnnotationValidations;
import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * Represents the type and contained data fields of a {@link Credential}.
@@ -42,10 +44,10 @@ public final class CredentialDescription implements Parcelable {
    private final String mType;

    /**
     * Flattened semicolon separated keys of JSON values to match with requests.
     * Keys of elements to match with Credential requests.
     */
    @NonNull
    private final String mFlattenedRequestString;
    private final Set<String> mSupportedElementKeys;

    /**
     * The credential entries to be used in the UI.
@@ -57,8 +59,7 @@ public final class CredentialDescription implements Parcelable {
     * Constructs a {@link CredentialDescription}.
     *
     * @param type the type of the credential returned.
     * @param flattenedRequestString flattened semicolon separated keys of JSON values
     *                              to match with requests.
     * @param supportedElementKeys Keys of elements to match with Credential requests.
     * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the
     *                          account selector if a credential matches with this description.
     *                          Each entry contains information to be displayed within an
@@ -68,10 +69,10 @@ public final class CredentialDescription implements Parcelable {
     * @throws IllegalArgumentException If type is empty.
     */
    public CredentialDescription(@NonNull String type,
            @NonNull String flattenedRequestString,
            @NonNull Set<String> supportedElementKeys,
            @NonNull List<CredentialEntry> credentialEntries) {
        mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
        mFlattenedRequestString = Preconditions.checkStringNotEmpty(flattenedRequestString);
        mSupportedElementKeys = Objects.requireNonNull(supportedElementKeys);
        mCredentialEntries = Objects.requireNonNull(credentialEntries);
        Preconditions.checkArgument(credentialEntries.size()
                        <= MAX_ALLOWED_ENTRIES_PER_DESCRIPTION,
@@ -82,15 +83,15 @@ public final class CredentialDescription implements Parcelable {

    private CredentialDescription(@NonNull Parcel in) {
        String type = in.readString8();
        String flattenedRequestString = in.readString();
        List<String> descriptions = in.createStringArrayList();
        List<CredentialEntry> entries = new ArrayList<>();
        in.readTypedList(entries, CredentialEntry.CREATOR);

        mType = type;
        AnnotationValidations.validate(android.annotation.NonNull.class, null, mType);
        mFlattenedRequestString = flattenedRequestString;
        mSupportedElementKeys = new HashSet<>(descriptions);
        AnnotationValidations.validate(android.annotation.NonNull.class, null,
                mFlattenedRequestString);
                mSupportedElementKeys);
        mCredentialEntries = entries;
        AnnotationValidations.validate(android.annotation.NonNull.class, null,
                mCredentialEntries);
@@ -125,7 +126,7 @@ public final class CredentialDescription implements Parcelable {
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString8(mType);
        dest.writeString(mFlattenedRequestString);
        dest.writeStringList(mSupportedElementKeys.stream().toList());
        dest.writeTypedList(mCredentialEntries, flags);
    }

@@ -141,8 +142,8 @@ public final class CredentialDescription implements Parcelable {
     * Returns the flattened JSON string that will be matched with requests.
     */
    @NonNull
    public String getFlattenedRequestString() {
        return mFlattenedRequestString;
    public Set<String> getSupportedElementKeys() {
        return new HashSet<>(mSupportedElementKeys);
    }

    /**
@@ -155,18 +156,18 @@ public final class CredentialDescription implements Parcelable {

    /**
     * {@link CredentialDescription#mType} and
     * {@link CredentialDescription#mFlattenedRequestString} are enough for hashing. Constructor
     * {@link CredentialDescription#mSupportedElementKeys} are enough for hashing. Constructor
     * enforces {@link CredentialEntry} to have the same type and
     * {@link android.app.slice.Slice} contained by the entry can not be hashed.
     */
    @Override
    public int hashCode() {
        return Objects.hash(mType, mFlattenedRequestString);
        return Objects.hash(mType, mSupportedElementKeys);
    }

    /**
     * {@link CredentialDescription#mType} and
     * {@link CredentialDescription#mFlattenedRequestString} are enough for equality check.
     * {@link CredentialDescription#mSupportedElementKeys} are enough for equality check.
     */
    @Override
    public boolean equals(Object obj) {
@@ -175,6 +176,6 @@ public final class CredentialDescription implements Parcelable {
        }
        CredentialDescription other = (CredentialDescription) obj;
        return mType.equals(other.mType)
                && mFlattenedRequestString.equals(other.mFlattenedRequestString);
                && mSupportedElementKeys.equals(other.mSupportedElementKeys);
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -43,12 +43,12 @@ import java.util.Set;
public final class CredentialOption implements Parcelable {

    /**
     * Bundle key to the flattened version of the JSON request string. Framework will use this key
     * Bundle key to the list of elements keys supported/requested. Framework will use this key
     * to determine which types of Credentials will utilize Credential Registry when filtering
     * Credential Providers to ping.
     */
    public static final String FLATTENED_REQUEST = "android.credentials"
            + ".GetCredentialOption.FLATTENED_REQUEST_STRING";
    public static final String SUPPORTED_ELEMENT_KEYS = "android.credentials"
            + ".GetCredentialOption.SUPPORTED_ELEMENT_KEYS";

    /**
     * The requested credential type.
+3 −2
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -126,11 +127,11 @@ public class CredentialManagerTest {
                null, List.of(Slice.HINT_TITLE)).build();
        mRegisterRequest = new RegisterCredentialDescriptionRequest(
                new CredentialDescription(Credential.TYPE_PASSWORD_CREDENTIAL,
                        "{ \"foo\": \"bar\" }",
                        new HashSet<>(List.of("{ \"foo\": \"bar\" }")),
                        List.of(new CredentialEntry(Credential.TYPE_PASSWORD_CREDENTIAL, slice))));
        mUnregisterRequest = new UnregisterCredentialDescriptionRequest(
                new CredentialDescription(Credential.TYPE_PASSWORD_CREDENTIAL,
                        "{ \"foo\": \"bar\" }",
                        new HashSet<>(List.of("{ \"foo\": \"bar\" }")),
                        List.of(new CredentialEntry(Credential.TYPE_PASSWORD_CREDENTIAL, slice))));

        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+18 −30
Original line number Diff line number Diff line
@@ -25,19 +25,16 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/** Contains information on what CredentialProvider has what provisioned Credential. */
public class CredentialDescriptionRegistry {

    private static final String FLAT_STRING_SPLIT_REGEX = ";";
    private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
    private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
    @GuardedBy("sLock")
@@ -53,15 +50,15 @@ public class CredentialDescriptionRegistry {
    /** Represents the results of a given query into the registry. */
    public static final class FilterResult {
        final String mPackageName;
        final String mFlattenedRequest;
        final Set<String> mElementKeys;
        final List<CredentialEntry> mCredentialEntries;

        @VisibleForTesting
        FilterResult(String packageName,
                String flattenedRequest,
                Set<String> elementKeys,
                List<CredentialEntry> credentialEntries) {
            mPackageName = packageName;
            mFlattenedRequest = flattenedRequest;
            mElementKeys = elementKeys;
            mCredentialEntries = credentialEntries;
        }
    }
@@ -166,18 +163,17 @@ public class CredentialDescriptionRegistry {
    /** Returns package names and entries of a CredentialProviders that can satisfy a given
     * {@link CredentialDescription}. */
    public Set<FilterResult> getFilteredResultForProvider(String packageName,
            String flatRequestString) {
            Set<String> requestedKeyElements) {
        Set<FilterResult> result = new HashSet<>();
        if (!mCredentialDescriptions.containsKey(packageName)) {
            return result;
        }
        Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
        Set<String> unflattenedRequestString = flatStringToSet(flatRequestString);
        for (CredentialDescription containedDescription: currentSet) {
            if (checkForMatch(flatStringToSet(containedDescription.getFlattenedRequestString()),
                    unflattenedRequestString)) {
            if (checkForMatch(containedDescription.getSupportedElementKeys(),
                    requestedKeyElements)) {
                result.add(new FilterResult(packageName,
                        containedDescription.getFlattenedRequestString(), containedDescription
                        containedDescription.getSupportedElementKeys(), containedDescription
                        .getCredentialEntries()));
            }
        }
@@ -186,18 +182,15 @@ public class CredentialDescriptionRegistry {

    /** Returns package names of CredentialProviders that can satisfy a given
     * {@link CredentialDescription}. */
    public Set<FilterResult> getMatchingProviders(Set<String> flatRequestStrings) {
    public Set<FilterResult> getMatchingProviders(Set<Set<String>> supportedElementKeys) {
        Set<FilterResult> result = new HashSet<>();
        Set<Set<String>> unflattenedRequestStrings = flatRequestStrings.stream().map(
                CredentialDescriptionRegistry::flatStringToSet).collect(Collectors.toSet());
        for (String packageName: mCredentialDescriptions.keySet()) {
            Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
            for (CredentialDescription containedDescription : currentSet) {
                if (canProviderSatisfyAny(flatStringToSet(containedDescription
                                .getFlattenedRequestString()),
                        unflattenedRequestStrings)) {
                if (canProviderSatisfyAny(containedDescription.getSupportedElementKeys(),
                        supportedElementKeys)) {
                    result.add(new FilterResult(packageName,
                            containedDescription.getFlattenedRequestString(), containedDescription
                            containedDescription.getSupportedElementKeys(), containedDescription
                            .getCredentialEntries()));
                }
            }
@@ -211,24 +204,19 @@ public class CredentialDescriptionRegistry {
        }
    }

    private static boolean canProviderSatisfyAny(Set<String> registeredUnflattenedStrings,
            Set<Set<String>> requestedUnflattenedStrings) {
        for (Set<String> requestedUnflattenedString : requestedUnflattenedStrings) {
            if (registeredUnflattenedStrings.containsAll(requestedUnflattenedString)) {
    private static boolean canProviderSatisfyAny(Set<String> registeredElementKeys,
            Set<Set<String>> requestedElementKeys) {
        for (Set<String> requestedUnflattenedString : requestedElementKeys) {
            if (registeredElementKeys.containsAll(requestedUnflattenedString)) {
                return true;
            }
        }
        return false;
    }

    static boolean checkForMatch(Set<String> registeredUnflattenedStrings,
            Set<String> requestedUnflattenedString) {
        return registeredUnflattenedStrings.containsAll(requestedUnflattenedString);
    }

    static Set<String> flatStringToSet(String flatString) {
        return new HashSet<>(Arrays
                .asList(flatString.split(FLAT_STRING_SPLIT_REGEX)));
    static boolean checkForMatch(Set<String> registeredElementKeys,
            Set<String> requestedElementKeys) {
        return registeredElementKeys.containsAll(requestedElementKeys);
    }

}
Loading