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

Commit 2f35fca0 authored by Omer Ozer's avatar Omer Ozer Committed by Android (Google) Code Review
Browse files

Merge "Add CredentialDescription registry API."

parents 287d0c32 aa4b959d
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