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
This commit is contained in:
Reema Bajwa
2022-09-06 05:49:21 +00:00
parent d88385c1c8
commit 9238ef2142
13 changed files with 1187 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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];
}
};
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,3 @@
package android.service.credentials;
parcelable GetCredentialsRequest;

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
package android.service.credentials;
parcelable GetCredentialsResponse;

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}