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

Commit aa4b959d authored by Omer Ozer's avatar Omer Ozer
Browse files

Add CredentialDescription registry API.

Bug: 260629338
CTS-Coverage-Bug: 265212839
API-Coverage-Bug: 265212839
Test: Local Build & Deployment
Change-Id: I6c30468eacb48425f6670b4d60bdc71d57a7e648
parent b7a44e84
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.credentials;

/**
 * @hide
 */
parcelable CredentialDescription;
 No newline at end of file
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.credentials;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.credentials.CredentialEntry;

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

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

/**
 * Represents the type and contained data fields of a {@link Credential}.
 * @hide
 */
public final class CredentialDescription implements Parcelable {

    /**
     * The credential type.
     */
    @NonNull
    private final String mType;

    /**
     * The flattened JSON string that will be matched with requests.
     */
    @NonNull
    private final String mFlattenedRequestString;

    /**
     * The entry to be used in the UI.
     */
    @NonNull
    private final List<CredentialEntry> mCredentialEntries;

    /**
     * Constructs a {@link CredentialDescription}.
     *
     * @param type the type of the credential returned.
     * @param flattenedRequestString flattened JSON string that will be matched with requests.
     * @param credentialEntries a list of {@link  CredentialEntry}s that have been returned
     *                          to the developer upon credential creation.
     *
     * @throws IllegalArgumentException If type is empty.
     */
    public CredentialDescription(@NonNull String type,
            @NonNull String flattenedRequestString,
            @NonNull List<CredentialEntry> credentialEntries) {
        mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
        mFlattenedRequestString = Preconditions.checkStringNotEmpty(flattenedRequestString);
        mCredentialEntries = Objects.requireNonNull(credentialEntries);
    }

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

        mType = type;
        AnnotationValidations.validate(android.annotation.NonNull.class, null, mType);
        mFlattenedRequestString = flattenedRequestString;
        AnnotationValidations.validate(android.annotation.NonNull.class, null,
                mFlattenedRequestString);
        mCredentialEntries = entries;
        AnnotationValidations.validate(android.annotation.NonNull.class, null,
                mCredentialEntries);
    }

    public static final @NonNull Parcelable.Creator<CredentialDescription> CREATOR =
            new Parcelable.Creator<CredentialDescription>() {
                @Override
                public CredentialDescription createFromParcel(Parcel in) {
                    return new CredentialDescription(in);
                }

                @Override
                public CredentialDescription[] newArray(int size) {
                    return new CredentialDescription[size];
                }
            };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(mType);
        dest.writeString(mFlattenedRequestString);
        dest.writeTypedList(mCredentialEntries, flags);
    }

    @NonNull
    public String getType() {
        return mType;
    }

    @NonNull
    public String getFlattenedRequestString() {
        return mFlattenedRequestString;
    }

    @NonNull
    public List<CredentialEntry> getCredentialEntries() {
        return mCredentialEntries;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mType, mFlattenedRequestString);
    }

    @Override
    public boolean equals(Object obj) {
        return Objects.equals(mType, ((CredentialDescription) obj).getType())
                && Objects.equals(mFlattenedRequestString, ((CredentialDescription) obj).getType());
    }
}
+164 −0
Original line number Diff line number Diff line
@@ -62,6 +62,14 @@ public final class CredentialManager {
    public static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
            "enable_credential_manager";

    /**
     * Flag to enable and disable Credential Description api.
     *
     * @hide
     */
    private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
            "enable_credential_description_api";

    /**
     * @hide instantiated by ContextImpl.
     */
@@ -294,6 +302,112 @@ public final class CredentialManager {
                true);
    }

    /**
     * Returns whether the credential description api is enabled.
     *
     * @hide
     */
    public static boolean isCredentialDescriptionApiEnabled() {
        return DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false);
    }

    /**
     *  Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
     * a CredentialProvider has. This registry will then be used by
     * {@link #executeGetCredential(GetCredentialRequest, Activity,
            * CancellationSignal, Executor, OutcomeReceiver)} to determine where to
     * fetch the requested {@link Credential} from.
     *
     *
     * @param request the request data
     * @param cancellationSignal an optional signal that allows for cancelling this call
     * @param executor the callback will take place on this {@link Executor}
     * @param callback the callback invoked when the request succeeds or fails
     *
     * @throws {@link  UnsupportedOperationException} if the feature has not been enabled.
     *
     * @hide
     */
    public void registerCredentialDescription(
            @NonNull RegisterCredentialDescriptionRequest request,
            @Nullable CancellationSignal cancellationSignal,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {

        if (!isCredentialDescriptionApiEnabled()) {
            throw new UnsupportedOperationException("This API is not currently supported.");
        }

        requireNonNull(executor, "executor must not be null");
        requireNonNull(callback, "callback must not be null");

        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
            Log.w(TAG, "executeCreateCredential already canceled");
            return;
        }

        ICancellationSignal cancelRemote = null;
        try {
            cancelRemote = mService.registerCredentialDescription(request,
                    new RegisterCredentialDescriptionTransport(executor, callback),
                    mContext.getOpPackageName());
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

        if (cancellationSignal != null && cancelRemote != null) {
            cancellationSignal.setRemote(cancelRemote);
        }
    }


    /**
     *  Unregisters a {@link CredentialDescription} for an actively provisioned {@link Credential}
     * that has been registered previously.
     *
     *
     * @param request the request data
     * @param cancellationSignal an optional signal that allows for cancelling this call
     * @param executor the callback will take place on this {@link Executor}
     * @param callback the callback invoked when the request succeeds or fails
     *
     * @throws {@link  UnsupportedOperationException} if the feature has not been enabled.
     *
     * @hide
     */
    public void unRegisterCredentialDescription(
            @NonNull UnregisterCredentialDescriptionRequest request,
            @Nullable CancellationSignal cancellationSignal,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {

        if (!isCredentialDescriptionApiEnabled()) {
            throw new UnsupportedOperationException("This API is not currently supported.");
        }

        requireNonNull(executor, "executor must not be null");
        requireNonNull(callback, "callback must not be null");

        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
            Log.w(TAG, "executeCreateCredential already canceled");
            return;
        }

        ICancellationSignal cancelRemote = null;
        try {
            cancelRemote = mService.unRegisterCredentialDescription(request,
                    new UnregisterCredentialDescriptionTransport(executor, callback),
                    mContext.getOpPackageName());
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

        if (cancellationSignal != null && cancelRemote != null) {
            cancellationSignal.setRemote(cancelRemote);
        }
    }

    private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
        // TODO: listen for cancellation to release callback.

@@ -455,4 +569,54 @@ public final class CredentialManager {
                    () -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
        }
    }

    private static class RegisterCredentialDescriptionTransport
            extends IRegisterCredentialDescriptionCallback.Stub {

        private final Executor mExecutor;
        private final OutcomeReceiver<Void, RegisterCredentialDescriptionException> mCallback;

        private RegisterCredentialDescriptionTransport(Executor executor,
                OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
            mExecutor = executor;
            mCallback = callback;
        }

        @Override
        public void onResponse() {
            mCallback.onResult(null);
        }

        @Override
        public void onError(String errorCode, String message) {
            mExecutor.execute(
                    () -> mCallback.onError(new RegisterCredentialDescriptionException(errorCode,
                            message)));
        }
    }

    private static class UnregisterCredentialDescriptionTransport
            extends IUnregisterCredentialDescriptionCallback.Stub {

        private final Executor mExecutor;
        private final OutcomeReceiver<Void, UnregisterCredentialDescriptionException> mCallback;

        private UnregisterCredentialDescriptionTransport(Executor executor,
                OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
            mExecutor = executor;
            mCallback = callback;
        }

        @Override
        public void onResponse() {
            mCallback.onResult(null);
        }

        @Override
        public void onError(String errorCode, String message) {
            mExecutor.execute(
                    () -> mCallback.onError(new UnregisterCredentialDescriptionException(errorCode,
                            message)));
        }
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -21,10 +21,14 @@ import java.util.List;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
import android.credentials.GetCredentialRequest;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
import android.credentials.IRegisterCredentialDescriptionCallback;
import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.os.ICancellationSignal;

@@ -44,4 +48,9 @@ interface ICredentialManager {
    @nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);

    void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);

    @nullable ICancellationSignal registerCredentialDescription(in RegisterCredentialDescriptionRequest request, in IRegisterCredentialDescriptionCallback callback, String callingPackage);

    @nullable ICancellationSignal unRegisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, in IUnregisterCredentialDescriptionCallback callback, String callingPackage);
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.credentials;

/**
 * Listener for an registerCredentialDescription request.
 *
 * @hide
 */
interface IRegisterCredentialDescriptionCallback {
    oneway void onResponse();
    oneway void onError(String errorCode, String message);
}
 No newline at end of file
Loading