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

Commit 5fea7f67 authored by Becca Hughes's avatar Becca Hughes
Browse files

Add SetEnabledProviders API method for Credential Manager

Adds SetEnabledProviders API which is a SystemApi used
by Settings to set the list of enabled providers.

Test: make
Bug: 253157179
CTS-Coverage-Bug: 247549381
Change-Id: Ib215c66ab20c9b71a8d5f3d8734f229c93a4a3a2
parent e4102ab5
Loading
Loading
Loading
Loading
+103 −35
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.Activity;
import android.app.PendingIntent;
@@ -32,6 +33,7 @@ import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.util.Log;

import java.util.List;
import java.util.concurrent.Executor;

/**
@@ -40,9 +42,9 @@ import java.util.concurrent.Executor;
 * <p>Note that an application should call the Jetpack CredentialManager apis instead of directly
 * calling these framework apis.
 *
 * <p>The CredentialManager apis launch framework UI flows for a user to
 * register a new credential or to consent to a saved credential from supported credential
 * providers, which can then be used to authenticate to the app.
 * <p>The CredentialManager apis launch framework UI flows for a user to register a new credential
 * or to consent to a saved credential from supported credential providers, which can then be used
 * to authenticate to the app.
 */
@SystemService(Context.CREDENTIAL_SERVICE)
public final class CredentialManager {
@@ -76,8 +78,7 @@ public final class CredentialManager {
            @NonNull Activity activity,
            @Nullable CancellationSignal cancellationSignal,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OutcomeReceiver<
                    GetCredentialResponse, GetCredentialException> callback) {
            @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
        requireNonNull(request, "request must not be null");
        requireNonNull(activity, "activity must not be null");
        requireNonNull(executor, "executor must not be null");
@@ -90,7 +91,8 @@ public final class CredentialManager {

        ICancellationSignal cancelRemote = null;
        try {
            cancelRemote = mService.executeGetCredential(
            cancelRemote =
                    mService.executeGetCredential(
                            request,
                            new GetCredentialTransport(activity, executor, callback),
                            mContext.getOpPackageName());
@@ -106,8 +108,8 @@ public final class CredentialManager {
    /**
     * Launches the necessary flows to register an app credential for the user.
     *
     * <p>The execution can potentially launch UI flows to collect user consent to creating
     * or storing the new credential, etc.
     * <p>The execution can potentially launch UI flows to collect user consent to creating or
     * storing the new credential, etc.
     *
     * @param request the request specifying type(s) of credentials to get from the user
     * @param activity the activity used to launch any UI needed
@@ -120,8 +122,8 @@ public final class CredentialManager {
            @NonNull Activity activity,
            @Nullable CancellationSignal cancellationSignal,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OutcomeReceiver<
                    CreateCredentialResponse, CreateCredentialException> callback) {
            @NonNull
                    OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
        requireNonNull(request, "request must not be null");
        requireNonNull(activity, "activity must not be null");
        requireNonNull(executor, "executor must not be null");
@@ -134,7 +136,9 @@ public final class CredentialManager {

        ICancellationSignal cancelRemote = null;
        try {
            cancelRemote = mService.executeCreateCredential(request,
            cancelRemote =
                    mService.executeCreateCredential(
                            request,
                            new CreateCredentialTransport(activity, executor, callback),
                            mContext.getOpPackageName());
        } catch (RemoteException e) {
@@ -149,10 +153,10 @@ public final class CredentialManager {
    /**
     * Clears the current user credential state from all credential providers.
     *
     * You should invoked this api after your user signs out of your app to notify all credential
     * <p>You should invoked this api after your user signs out of your app to notify all credential
     * providers that any stored credential session for the given app should be cleared.
     *
     * A credential provider may have stored an active credential session and use it to limit
     * <p>A credential provider may have stored an active credential session and use it to limit
     * sign-in options for future get-credential calls. For example, it may prioritize the active
     * credential over any other available credential. When your user explicitly signs out of your
     * app and in order to get the holistic sign-in options the next time, you should call this API
@@ -178,7 +182,9 @@ public final class CredentialManager {

        ICancellationSignal cancelRemote = null;
        try {
            cancelRemote = mService.clearCredentialState(request,
            cancelRemote =
                    mService.clearCredentialState(
                            request,
                            new ClearCredentialStateTransport(executor, callback),
                            mContext.getOpPackageName());
        } catch (RemoteException e) {
@@ -190,15 +196,44 @@ public final class CredentialManager {
        }
    }

    /**
     * Sets a list of all user configurable credential providers registered on the system. This API
     * is intended for settings apps.
     *
     * @param providers the list of enabled providers
     * @param userId the user ID to configure credential manager for
     * @param executor the callback will take place on this {@link Executor}
     * @param callback the callback invoked when the request succeeds or fails
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    public void setEnabledProviders(
            @NonNull List<String> providers,
            int userId,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OutcomeReceiver<Void, SetEnabledProvidersException> callback) {
        requireNonNull(executor, "executor must not be null");
        requireNonNull(callback, "callback must not be null");
        requireNonNull(providers, "providers must not be null");

        try {
            mService.setEnabledProviders(
                    providers, userId, new SetEnabledProvidersTransport(executor, callback));
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

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

        private final Activity mActivity;
        private final Executor mExecutor;
        private final OutcomeReceiver<
                GetCredentialResponse, GetCredentialException> mCallback;
        private final OutcomeReceiver<GetCredentialResponse, GetCredentialException> mCallback;

        private GetCredentialTransport(Activity activity, Executor executor,
        private GetCredentialTransport(
                Activity activity,
                Executor executor,
                OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
            mActivity = activity;
            mExecutor = executor;
@@ -210,8 +245,10 @@ public final class CredentialManager {
            try {
                mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
            } catch (IntentSender.SendIntentException e) {
                Log.e(TAG, "startIntentSender() failed for intent:"
                        + pendingIntent.getIntentSender(), e);
                Log.e(
                        TAG,
                        "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
                        e);
                // TODO: propagate the error.
            }
        }
@@ -233,10 +270,12 @@ public final class CredentialManager {

        private final Activity mActivity;
        private final Executor mExecutor;
        private final OutcomeReceiver<
                CreateCredentialResponse, CreateCredentialException> mCallback;
        private final OutcomeReceiver<CreateCredentialResponse, CreateCredentialException>
                mCallback;

        private CreateCredentialTransport(Activity activity, Executor executor,
        private CreateCredentialTransport(
                Activity activity,
                Executor executor,
                OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
            mActivity = activity;
            mExecutor = executor;
@@ -248,8 +287,10 @@ public final class CredentialManager {
            try {
                mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
            } catch (IntentSender.SendIntentException e) {
                Log.e(TAG, "startIntentSender() failed for intent:"
                        + pendingIntent.getIntentSender(), e);
                Log.e(
                        TAG,
                        "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
                        e);
                // TODO: propagate the error.
            }
        }
@@ -266,15 +307,14 @@ public final class CredentialManager {
        }
    }

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

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

        private ClearCredentialStateTransport(Executor executor,
                OutcomeReceiver<Void, ClearCredentialStateException> callback) {
        private ClearCredentialStateTransport(
                Executor executor, OutcomeReceiver<Void, ClearCredentialStateException> callback) {
            mExecutor = executor;
            mCallback = callback;
        }
@@ -290,4 +330,32 @@ public final class CredentialManager {
                    () -> mCallback.onError(new ClearCredentialStateException(errorType, message)));
        }
    }

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

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

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

        public void onResponse(Void result) {
            mExecutor.execute(() -> mCallback.onResult(result));
        }

        @Override
        public void onResponse() {
            mExecutor.execute(() -> mCallback.onResult(null));
        }

        @Override
        public void onError(String errorType, String message) {
            mExecutor.execute(
                    () -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -16,12 +16,15 @@

package android.credentials;

import java.util.List;

import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
import android.credentials.GetCredentialRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.os.ICancellationSignal;

/**
@@ -36,4 +39,6 @@ interface ICredentialManager {
    @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);

    @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);

    void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 setEnabledProviders request.
 *
 * @hide
 */
interface ISetEnabledProvidersCallback {
    oneway void onResponse();
    oneway void onError(String errorType, String message);
}
 No newline at end of file
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.annotation.Nullable;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;

import com.android.internal.util.Preconditions;

/**
 * Represents an error encountered during the {@link
 * CredentialManager#setEnabledProviders(CancellationSignal Executor, OutcomeReceiver)} operation.
 *
 * @hide
 */
public class SetEnabledProvidersException extends Exception {

    @NonNull public final String errorType;

    /**
     * Constructs a {@link SetEnabledProvidersException}.
     *
     * @throws IllegalArgumentException If errorType is empty.
     */
    public SetEnabledProvidersException(@NonNull String errorType, @Nullable String message) {
        this(errorType, message, null);
    }

    /**
     * Constructs a {@link SetEnabledProvidersException}.
     *
     * @throws IllegalArgumentException If errorType is empty.
     */
    public SetEnabledProvidersException(
            @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
        super(message, cause);
        this.errorType =
                Preconditions.checkStringNotEmpty(errorType, "errorType must not be empty");
    }

    /**
     * Constructs a {@link SetEnabledProvidersException}.
     *
     * @throws IllegalArgumentException If errorType is empty.
     */
    public SetEnabledProvidersException(@NonNull String errorType, @Nullable Throwable cause) {
        this(errorType, null, cause);
    }

    /**
     * Constructs a {@link SetEnabledProvidersException}.
     *
     * @throws IllegalArgumentException If errorType is empty.
     */
    public SetEnabledProvidersException(@NonNull String errorType) {
        this(errorType, null, null);
    }
}
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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;

parcelable SetEnabledProvidersRequest;
 No newline at end of file
Loading