Add provider facing GET APIs for Credential Manager.
Credential Manager is a set of APIs that allows developers to get and
create credentials. These credentials are sourced from providers that
are registered with the android framework.
Test: Built and Deployed on device locally.
Bug: 247545196
CTS-Coverage-Bug: 247549381
Change-Id: If5b6569810be30ec6f7997e9161fe9b85d286cfc
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
new file mode 100644
index 0000000..186b2a6
--- /dev/null
+++ b/core/java/android/service/credentials/Action.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * An action defined by the provider that intents into the provider's app for specific
+ * user actions.
+ *
+ * @hide
+ */
+public final class Action implements Parcelable {
+ /** Info to be displayed with this action on the UI. */
+ private final @NonNull Slice mInfo;
+ /**
+ * The pending intent to be invoked when the user selects this action.
+ */
+ private final @NonNull PendingIntent mPendingIntent;
+
+ /**
+ * Constructs an action to be displayed on the UI.
+ *
+ * @param actionInfo The info to be displayed along with this action.
+ * @param pendingIntent The intent to be invoked when the user selects this action.
+ * @throws NullPointerException If {@code actionInfo}, or {@code pendingIntent} is null.
+ */
+ public Action(@NonNull Slice actionInfo, @NonNull PendingIntent pendingIntent) {
+ Objects.requireNonNull(actionInfo, "actionInfo must not be null");
+ Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+ mInfo = actionInfo;
+ mPendingIntent = pendingIntent;
+ }
+
+ private Action(@NonNull Parcel in) {
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ }
+
+ public static final @NonNull Creator<Action> CREATOR = new Creator<Action>() {
+ @Override
+ public Action createFromParcel(@NonNull Parcel in) {
+ return new Action(in);
+ }
+
+ @Override
+ public Action[] newArray(int size) {
+ return new Action[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Returns the action info as a {@link Slice} object, to be displayed on the UI.
+ */
+ public @NonNull Slice getActionInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the {@link PendingIntent} to be invoked when the action is selected.
+ */
+ public @NonNull PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+}
diff --git a/core/java/android/service/credentials/Credential.java b/core/java/android/service/credentials/Credential.java
new file mode 100644
index 0000000..7d5da8a
--- /dev/null
+++ b/core/java/android/service/credentials/Credential.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import static java.util.Objects.requireNonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A Credential object that contains type specific data that is returned from the credential
+ * provider to the framework. Framework then converts it to an app facing representation and
+ * returns to the calling app.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+ /** The type of this credential. */
+ private final @NonNull String mType;
+
+ /** The data associated with this credential. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a credential object.
+ *
+ * @param type The type of the credential.
+ * @param data The data of the credential that is passed back to the framework, and eventually
+ * to the calling app.
+ * @throws NullPointerException If {@code data} is null.
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ */
+ public Credential(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ this.mType = type;
+ this.mData = data;
+ }
+
+ private Credential(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ /**
+ * Returns the type of the credential.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the data associated with the credential.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ public static final @NonNull Creator<Credential> CREATOR = new Creator<Credential>() {
+ @Override
+ public Credential createFromParcel(@NonNull Parcel in) {
+ return new Credential(in);
+ }
+
+ @Override
+ public Credential[] newArray(int size) {
+ return new Credential[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ dest.writeBundle(mData);
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
new file mode 100644
index 0000000..b49215a
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A credential entry that is displayed on the account selector UI. Each entry corresponds to
+ * something that the user can select.
+ *
+ * @hide
+ */
+public final class CredentialEntry implements Parcelable {
+ /** The type of the credential entry to be shown on the UI. */
+ private final @NonNull String mType;
+
+ /** The info to be displayed along with this credential entry on the UI. */
+ private final @NonNull Slice mInfo;
+
+ /** The pending intent to be invoked when this credential entry is selected. */
+ private final @Nullable PendingIntent mPendingIntent;
+
+ /**
+ * The underlying credential to be returned to the app when the user selects
+ * this credential entry.
+ */
+ private final @Nullable Credential mCredential;
+
+ /** A flag denoting whether auto-select is enabled for this entry. */
+ private final @NonNull boolean mAutoSelectAllowed;
+
+ private CredentialEntry(@NonNull String type, @NonNull Slice entryInfo,
+ @Nullable PendingIntent pendingIntent, @Nullable Credential credential,
+ @NonNull boolean autoSeletAllowed) {
+ mType = type;
+ mInfo = entryInfo;
+ mPendingIntent = pendingIntent;
+ mCredential = credential;
+ mAutoSelectAllowed = autoSeletAllowed;
+ }
+
+ private CredentialEntry(@NonNull Parcel in) {
+ mType = in.readString();
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mCredential = in.readParcelable(Credential.class.getClassLoader(),
+ Credential.class);
+ mAutoSelectAllowed = in.readBoolean();
+ }
+
+ public static final @NonNull Creator<CredentialEntry> CREATOR =
+ new Creator<CredentialEntry>() {
+ @Override
+ public CredentialEntry createFromParcel(@NonNull Parcel in) {
+ return new CredentialEntry(in);
+ }
+
+ @Override
+ public CredentialEntry[] newArray(int size) {
+ return new CredentialEntry[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ mCredential.writeToParcel(dest, flags);
+ dest.writeBoolean(mAutoSelectAllowed);
+ }
+
+ /**
+ * Returns the specific credential type of the entry.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the UI info to be displayed for this entry.
+ */
+ public @NonNull Slice getInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the pending intent to be invoked if the user selects this entry.
+ */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /**
+ * Returns the credential associated with this entry.
+ */
+ public @Nullable Credential getCredential() {
+ return mCredential;
+ }
+
+ /**
+ * Returns whether this entry can be auto selected if it is the only option for the user.
+ */
+ public @NonNull boolean isAutoSelectAllowed() {
+ return mAutoSelectAllowed;
+ }
+
+ /**
+ * Builder for {@link CredentialEntry}.
+ */
+ public static final class Builder {
+ private String mType;
+ private Slice mInfo;
+ private PendingIntent mPendingIntent;
+ private Credential mCredential;
+ private boolean mAutoSelectAllowed = false;
+
+ /**
+ * Builds the instance.
+ * @param type The type of credential underlying this credential entry.
+ * @param info The info to be displayed with this entry on the UI.
+ *
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ * @throws NullPointerException If {@code info} is null.
+ */
+ public Builder(@NonNull String type, @NonNull Slice info) {
+ mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ + "null, or empty");
+ mInfo = Objects.requireNonNull(info, "info must not be null");
+ }
+
+ /**
+ * Sets the pendingIntent to be invoked if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code credential} is already set. Must either set the
+ * {@code credential}, or the {@code pendingIntent}.
+ */
+ public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ Preconditions.checkState(pendingIntent != null && mCredential != null,
+ "credential is already set. Cannot set both the pendingIntent "
+ + "and the credential");
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the credential to be used, if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code pendingIntent} is already set. Must either set
+ * the {@code pendingIntent}, or the {@code credential}.
+ */
+ public @NonNull Builder setCredential(@Nullable Credential credential) {
+ Preconditions.checkState(credential != null && mPendingIntent != null,
+ "pendingIntent is already set. Cannot set both the "
+ + "pendingIntent and the credential");
+ mCredential = credential;
+ return this;
+ }
+
+ /**
+ * Sets whether the entry is allowed to be auto selected by the framework.
+ * The default value is set to false.
+ */
+ public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) {
+ mAutoSelectAllowed = autoSelectAllowed;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link CredentialEntry} instance.
+ *
+ * @throws NullPointerException If {@code info} is null.
+ * @throws IllegalArgumentException If {@code type} is null, or empty.
+ * @throws IllegalStateException If neither {@code pendingIntent} nor {@code credential}
+ * is set, or if both are set.
+ */
+ public @NonNull CredentialEntry build() {
+ Preconditions.checkState(mPendingIntent == null && mCredential == null,
+ "Either pendingIntent or credential must be set");
+ Preconditions.checkState(mPendingIntent != null && mCredential != null,
+ "Cannot set both the pendingIntent and credential");
+ return new CredentialEntry(mType, mInfo, mPendingIntent,
+ mCredential, mAutoSelectAllowed);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
new file mode 100644
index 0000000..e2a495d
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.app.Service;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Main service to be extended by credential providers, in order to return user credentials
+ * to the framework.
+ *
+ * @hide
+ */
+public abstract class CredentialProviderService extends Service {
+ private static final String TAG = "CredProviderService";
+ private Handler mHandler;
+
+ public static final String SERVICE_INTERFACE =
+ "android.service.credentials.CredentialProviderService";
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ public final @NonNull IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.i(TAG, "Failed to bind with intent: " + intent);
+ return null;
+ }
+
+ private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
+ @Override
+ public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
+ IGetCredentialsCallback callback) throws RemoteException {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+
+ mHandler.sendMessage(obtainMessage(
+ CredentialProviderService::onGetCredentials,
+ CredentialProviderService.this, request,
+ CancellationSignal.fromTransport(transport),
+ new GetCredentialsCallback(callback)
+ ));
+ }
+ };
+
+ /**
+ * Called by the android system to retrieve user credentials from the connected provider
+ * service.
+ * @param request The credential request for the provider to handle.
+ * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+ * the android system.
+ * @param callback Object used to relay the response of the credentials request.
+ */
+ public abstract void onGetCredentials(@NonNull GetCredentialsRequest request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull GetCredentialsCallback callback);
+}
diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsDisplayContent.java
new file mode 100644
index 0000000..106f322
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialsDisplayContent.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Content to be displayed on the account selector UI, including credential entries,
+ * actions etc.
+ *
+ * @hide
+ */
+public final class CredentialsDisplayContent implements Parcelable {
+ /** Header to be displayed on the UI. */
+ private final @Nullable CharSequence mHeader;
+
+ /** List of credential entries to be displayed on the UI. */
+ private final @NonNull List<CredentialEntry> mCredentialEntries;
+
+ /** List of provider actions to be displayed on the UI. */
+ private final @NonNull List<Action> mActions;
+
+ private CredentialsDisplayContent(@Nullable CharSequence header,
+ @NonNull List<CredentialEntry> credentialEntries,
+ @NonNull List<Action> actions) {
+ mHeader = header;
+ mCredentialEntries = credentialEntries;
+ mActions = actions;
+ }
+
+ private CredentialsDisplayContent(@NonNull Parcel in) {
+ mHeader = in.readCharSequence();
+ mCredentialEntries = in.createTypedArrayList(CredentialEntry.CREATOR);
+ mActions = in.createTypedArrayList(Action.CREATOR);
+ }
+
+ public static final @NonNull Creator<CredentialsDisplayContent> CREATOR =
+ new Creator<CredentialsDisplayContent>() {
+ @Override
+ public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) {
+ return new CredentialsDisplayContent(in);
+ }
+
+ @Override
+ public CredentialsDisplayContent[] newArray(int size) {
+ return new CredentialsDisplayContent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeCharSequence(mHeader);
+ dest.writeTypedList(mCredentialEntries);
+ dest.writeTypedList(mActions);
+ }
+
+ /**
+ * Returns the header to be displayed on the UI.
+ */
+ public @Nullable CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /**
+ * Returns the list of credential entries to be displayed on the UI.
+ */
+ public @NonNull List<CredentialEntry> getCredentialEntries() {
+ return mCredentialEntries;
+ }
+
+ /**
+ * Returns the list of actions to be displayed on the UI.
+ */
+ public @NonNull List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
+ * Builds an instance of {@link CredentialsDisplayContent}.
+ */
+ public static final class Builder {
+ private CharSequence mHeader = null;
+ private List<CredentialEntry> mCredentialEntries = new ArrayList<>();
+ private List<Action> mActions = new ArrayList<>();
+
+ /**
+ * Sets the header to be displayed on the UI.
+ */
+ public @NonNull Builder setHeader(@Nullable CharSequence header) {
+ mHeader = header;
+ return this;
+ }
+
+ /**
+ * Adds a {@link CredentialEntry} to the list of entries to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If the {@code credentialEntry} is null.
+ */
+ public @NonNull Builder addCredentialEntry(@NonNull CredentialEntry credentialEntry) {
+ mCredentialEntries.add(Objects.requireNonNull(credentialEntry));
+ return this;
+ }
+
+ /**
+ * Adds an {@link Action} to the list of actions to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If {@code action} is null.
+ */
+ public @NonNull Builder addAction(@NonNull Action action) {
+ mActions.add(Objects.requireNonNull(action, "action must not be null"));
+ return this;
+ }
+
+ /**
+ * Sets the list of actions to be displayed on the UI.
+ *
+ * @throws NullPointerException If {@code actions} is null, or any of its elements
+ * is null.
+ */
+ public @NonNull Builder setActions(@NonNull List<Action> actions) {
+ mActions = Preconditions.checkCollectionElementsNotNull(actions,
+ "actions");
+ return this;
+ }
+
+ /**
+ * Sets the list of credential entries to be displayed on the
+ * account selector UI.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null, or any of its
+ * elements is null.
+ */
+ public @NonNull Builder setCredentialEntries(
+ @NonNull List<CredentialEntry> credentialEntries) {
+ mCredentialEntries = Preconditions.checkCollectionElementsNotNull(
+ credentialEntries,
+ "credentialEntries");
+ return this;
+ }
+
+ /**
+ * Builds a {@link GetCredentialsResponse} instance.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null.
+ * @throws IllegalStateException if both {@code credentialEntries} and
+ * {@code actions} are empty.
+ */
+ public @NonNull CredentialsDisplayContent build() {
+ if (mCredentialEntries != null && mCredentialEntries.isEmpty()
+ && mActions != null && mActions.isEmpty()) {
+ throw new IllegalStateException("credentialEntries and actions must not both "
+ + "be empty");
+ }
+ return new CredentialsDisplayContent(mHeader, mCredentialEntries, mActions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialOption.java b/core/java/android/service/credentials/GetCredentialOption.java
new file mode 100644
index 0000000..c6cda1d
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialOption.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A type specific credential request, containing the associated data to be used for
+ * retrieving credentials.
+ *
+ * @hide
+ */
+public final class GetCredentialOption implements Parcelable {
+ /** The type of credential requested. */
+ private final @NonNull String mType;
+
+ /** The data associated with the request. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a new instance of {@link GetCredentialOption}
+ *
+ * @throws IllegalArgumentException If {@code type} string is null or empty.
+ * @throws NullPointerException If {@code data} is null.
+ */
+ public GetCredentialOption(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ mType = type;
+ mData = data;
+ }
+
+ /**
+ * Returns the data associated with this credential request option.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the type associated with this credential request option.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ private GetCredentialOption(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mType);
+ dest.writeBundle(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<GetCredentialOption> CREATOR =
+ new Creator<GetCredentialOption>() {
+ @Override
+ public GetCredentialOption createFromParcel(@NonNull Parcel in) {
+ return new GetCredentialOption(in);
+ }
+
+ @Override
+ public GetCredentialOption[] newArray(int size) {
+ return new GetCredentialOption[size];
+ }
+ };
+}
diff --git a/core/java/android/service/credentials/GetCredentialsCallback.java b/core/java/android/service/credentials/GetCredentialsCallback.java
new file mode 100644
index 0000000..0e81b8f
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link GetCredentialsRequest}.
+ *
+ * @hide
+ */
+public final class GetCredentialsCallback {
+
+ private static final String TAG = "GetCredentialsCallback";
+
+ private final IGetCredentialsCallback mCallback;
+
+ /** @hide */
+ public GetCredentialsCallback(@NonNull IGetCredentialsCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Invoked on a successful response for {@link GetCredentialsRequest}
+ * @param response The response from the credential provider.
+ */
+ public void onSuccess(@Nullable GetCredentialsResponse response) {
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Invoked on a failure response for {@link GetCredentialsRequest}
+ * @param errorCode The code defining the kind of error.
+ * @param message The message corresponding to the failure.
+ */
+ public void onFailure(int errorCode, @Nullable CharSequence message) {
+ Log.w(TAG, "onFailure: " + message);
+ try {
+ mCallback.onFailure(errorCode, message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.aidl b/core/java/android/service/credentials/GetCredentialsRequest.aidl
new file mode 100644
index 0000000..b309d69
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java
new file mode 100644
index 0000000..cf7c283
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request for getting user's credentials from a given credential provider.
+ *
+ * @hide
+ */
+public final class GetCredentialsRequest implements Parcelable {
+ /** Calling package of the app requesting for credentials. */
+ private final @NonNull String mCallingPackage;
+
+ /**
+ * List of credential options. Each {@link GetCredentialOption} object holds parameters to
+ * be used for retrieving specific type of credentials.
+ */
+ private final @NonNull List<GetCredentialOption> mGetCredentialOptions;
+
+ private GetCredentialsRequest(@NonNull String callingPackage,
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ this.mCallingPackage = callingPackage;
+ this.mGetCredentialOptions = getCredentialOptions;
+ }
+
+ private GetCredentialsRequest(@NonNull Parcel in) {
+ mCallingPackage = in.readString16NoHelper();
+ mGetCredentialOptions = in.createTypedArrayList(GetCredentialOption.CREATOR);
+ }
+
+ public static final @NonNull Creator<GetCredentialsRequest> CREATOR =
+ new Creator<GetCredentialsRequest>() {
+ @Override
+ public GetCredentialsRequest createFromParcel(Parcel in) {
+ return new GetCredentialsRequest(in);
+ }
+
+ @Override
+ public GetCredentialsRequest[] newArray(int size) {
+ return new GetCredentialsRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mCallingPackage);
+ dest.writeTypedList(mGetCredentialOptions);
+ }
+
+ /**
+ * Returns the calling package of the app requesting credentials.
+ */
+ public @NonNull String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /**
+ * Returns the list of type specific credential options to return credentials for.
+ */
+ public @NonNull List<GetCredentialOption> getGetCredentialOptions() {
+ return mGetCredentialOptions;
+ }
+
+ /**
+ * Builder for {@link GetCredentialsRequest}.
+ */
+ public static final class Builder {
+ private String mCallingPackage;
+ private List<GetCredentialOption> mGetCredentialOptions = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ * @param callingPackage The calling package of the app requesting credentials.
+ *
+ * @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
+ */
+ public Builder(@NonNull String callingPackage) {
+ mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+ }
+
+ /**
+ * Sets the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} itself or any of its
+ * elements is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty.
+ */
+ public @NonNull Builder setGetCredentialOptions(
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ Preconditions.checkCollectionElementsNotNull(mGetCredentialOptions,
+ "getCredentialOptions");
+ mGetCredentialOptions = getCredentialOptions;
+ return this;
+ }
+
+ /**
+ * Adds a single {@link GetCredentialOption} object to the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOption} is null.
+ */
+ public @NonNull Builder addGetCredentialOption(
+ @NonNull GetCredentialOption getCredentialOption) {
+ Objects.requireNonNull(getCredentialOption,
+ "getCredentialOption must not be null");
+ mGetCredentialOptions.add(getCredentialOption);
+ return this;
+ }
+
+ /**
+ * Builds a new {@link GetCredentialsRequest} instance.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty, or if
+ * {@code callingPackage} is null or empty.
+ */
+ public @NonNull GetCredentialsRequest build() {
+ Preconditions.checkStringNotEmpty(mCallingPackage,
+ "Must set the calling package");
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ return new GetCredentialsRequest(mCallingPackage, mGetCredentialOptions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.aidl b/core/java/android/service/credentials/GetCredentialsResponse.aidl
new file mode 100644
index 0000000..0d8c635
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java
new file mode 100644
index 0000000..293867b
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 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.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Response from a credential provider, containing credential entries and other associated
+ * data to be shown on the account selector UI.
+ *
+ * @hide
+ */
+public final class GetCredentialsResponse implements Parcelable {
+ /** Content to be used for the UI. */
+ private final @Nullable CredentialsDisplayContent mCredentialsDisplayContent;
+
+ /**
+ * Authentication action that must be launched and completed before showing any content
+ * from the provider.
+ */
+ private final @Nullable Action mAuthenticationAction;
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with an authentication action set.
+ * Providers must use this method when no content can be shown before authentication.
+ *
+ * @throws NullPointerException If {@code authenticationAction} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithAuthentication(
+ @NonNull Action authenticationAction) {
+ Objects.requireNonNull(authenticationAction,
+ "authenticationAction must not be null");
+ return new GetCredentialsResponse(null, authenticationAction);
+ }
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI.
+ * Providers must use this method when there is content to be shown without top level
+ * authentication required.
+ *
+ * @throws NullPointerException If {@code credentialsDisplayContent} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithDisplayContent(
+ @NonNull CredentialsDisplayContent credentialsDisplayContent) {
+ Objects.requireNonNull(credentialsDisplayContent,
+ "credentialsDisplayContent must not be null");
+ return new GetCredentialsResponse(credentialsDisplayContent, null);
+ }
+
+ private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent,
+ @Nullable Action authenticationAction) {
+ mCredentialsDisplayContent = credentialsDisplayContent;
+ mAuthenticationAction = authenticationAction;
+ }
+
+ private GetCredentialsResponse(@NonNull Parcel in) {
+ mCredentialsDisplayContent = in.readParcelable(CredentialsDisplayContent.class
+ .getClassLoader(), CredentialsDisplayContent.class);
+ mAuthenticationAction = in.readParcelable(Action.class.getClassLoader(), Action.class);
+ }
+
+ public static final @NonNull Creator<GetCredentialsResponse> CREATOR =
+ new Creator<GetCredentialsResponse>() {
+ @Override
+ public GetCredentialsResponse createFromParcel(Parcel in) {
+ return new GetCredentialsResponse(in);
+ }
+
+ @Override
+ public GetCredentialsResponse[] newArray(int size) {
+ return new GetCredentialsResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mCredentialsDisplayContent, flags);
+ dest.writeParcelable(mAuthenticationAction, flags);
+ }
+
+ /**
+ * Returns whether the response contains a top level authentication action.
+ */
+ public @NonNull boolean isAuthenticationActionSet() {
+ return mAuthenticationAction != null;
+ }
+
+ /**
+ * Returns the authentication action to be invoked before any other content
+ * can be shown to the user.
+ */
+ public @NonNull Action getAuthenticationAction() {
+ return mAuthenticationAction;
+ }
+
+ /**
+ * Returns the credentialDisplayContent that does not require authentication, and
+ * can be shown to the user on the account selector UI.
+ */
+ public @NonNull CredentialsDisplayContent getCredentialsDisplayContent() {
+ return mCredentialsDisplayContent;
+ }
+}
diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl
new file mode 100644
index 0000000..0b2a9d6
--- /dev/null
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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.service.credentials;
+
+import android.os.ICancellationSignal;
+import android.service.credentials.GetCredentialsRequest;
+import android.service.credentials.IGetCredentialsCallback;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICredentialProviderService {
+ void onGetCredentials(in GetCredentialsRequest request, in ICancellationSignal transport, in IGetCredentialsCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/credentials/IGetCredentialsCallback.aidl b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
new file mode 100644
index 0000000..6e20c55
--- /dev/null
+++ b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.GetCredentialsResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface IGetCredentialsCallback {
+ void onSuccess(in GetCredentialsResponse response);
+ void onFailure(int errorCode, in CharSequence message);
+}
\ No newline at end of file