Merge "Unhide Credential Manager provider APIs Test: Built & deployed locally Bug: 253155222 CTS-Coverage-Bug:247549381"

This commit is contained in:
Reema Bajwa
2022-11-29 11:11:56 +00:00
committed by Android (Google) Code Review
25 changed files with 669 additions and 352 deletions

View File

@@ -39328,6 +39328,149 @@ package android.service.controls.templates {
}
package android.service.credentials {
public final class Action implements android.os.Parcelable {
ctor public Action(@NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent);
method public int describeContents();
method @NonNull public android.app.PendingIntent getPendingIntent();
method @NonNull public android.app.slice.Slice getSlice();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.Action> CREATOR;
}
public final class BeginCreateCredentialRequest implements android.os.Parcelable {
ctor public BeginCreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
method @NonNull public String getCallingPackage();
method @NonNull public android.os.Bundle getData();
method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginCreateCredentialRequest> CREATOR;
}
public final class BeginCreateCredentialResponse implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.service.credentials.CreateEntry> getCreateEntries();
method @Nullable public android.service.credentials.CreateEntry getRemoteCreateEntry();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginCreateCredentialResponse> CREATOR;
}
public static final class BeginCreateCredentialResponse.Builder {
ctor public BeginCreateCredentialResponse.Builder();
method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder addCreateEntry(@NonNull android.service.credentials.CreateEntry);
method @NonNull public android.service.credentials.BeginCreateCredentialResponse build();
method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setCreateEntries(@NonNull java.util.List<android.service.credentials.CreateEntry>);
method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.CreateEntry);
}
public final class CreateCredentialRequest implements android.os.Parcelable {
ctor public CreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
method @NonNull public String getCallingPackage();
method @NonNull public android.os.Bundle getData();
method @NonNull public String getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CreateCredentialRequest> CREATOR;
}
public final class CreateEntry implements android.os.Parcelable {
ctor public CreateEntry(@NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent);
method public int describeContents();
method @NonNull public android.app.PendingIntent getPendingIntent();
method @NonNull public android.app.slice.Slice getSlice();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CreateEntry> CREATOR;
}
public final class CredentialEntry implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.credentials.Credential getCredential();
method @Nullable public android.app.PendingIntent getPendingIntent();
method @NonNull public android.app.slice.Slice getSlice();
method @NonNull public String getType();
method public boolean isAutoSelectAllowed();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CredentialEntry> CREATOR;
}
public static final class CredentialEntry.Builder {
ctor public CredentialEntry.Builder(@NonNull String, @NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent);
ctor public CredentialEntry.Builder(@NonNull String, @NonNull android.app.slice.Slice, @NonNull android.credentials.Credential);
method @NonNull public android.service.credentials.CredentialEntry build();
method @NonNull public android.service.credentials.CredentialEntry.Builder setAutoSelectAllowed(@NonNull boolean);
}
public class CredentialProviderException extends java.lang.Exception {
ctor public CredentialProviderException(int, @NonNull String, @NonNull Throwable);
ctor public CredentialProviderException(int, @NonNull String);
ctor public CredentialProviderException(int, @NonNull Throwable);
ctor public CredentialProviderException(int);
method public int getErrorCode();
field public static final int ERROR_UNKNOWN = 0; // 0x0
}
public abstract class CredentialProviderService extends android.app.Service {
ctor public CredentialProviderService();
method public abstract void onBeginCreateCredential(@NonNull android.service.credentials.BeginCreateCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.service.credentials.CredentialProviderException>);
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onGetCredentials(@NonNull android.service.credentials.GetCredentialsRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.GetCredentialsResponse,android.service.credentials.CredentialProviderException>);
field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
field public static final String EXTRA_CREATE_CREDENTIAL_REQUEST = "android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST";
field public static final String EXTRA_CREATE_CREDENTIAL_RESULT = "android.service.credentials.extra.CREATE_CREDENTIAL_RESULT";
field public static final String EXTRA_CREDENTIAL_RESULT = "android.service.credentials.extra.CREDENTIAL_RESULT";
field public static final String EXTRA_ERROR = "android.service.credentials.extra.ERROR";
field public static final String EXTRA_GET_CREDENTIALS_CONTENT_RESULT = "android.service.credentials.extra.GET_CREDENTIALS_CONTENT_RESULT";
field public static final String SERVICE_INTERFACE = "android.service.credentials.CredentialProviderService";
}
public final class CredentialsResponseContent implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.service.credentials.Action> getActions();
method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
method @Nullable public android.service.credentials.CredentialEntry getRemoteCredentialEntry();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CredentialsResponseContent> CREATOR;
}
public static final class CredentialsResponseContent.Builder {
ctor public CredentialsResponseContent.Builder();
method @NonNull public android.service.credentials.CredentialsResponseContent.Builder addAction(@NonNull android.service.credentials.Action);
method @NonNull public android.service.credentials.CredentialsResponseContent.Builder addCredentialEntry(@NonNull android.service.credentials.CredentialEntry);
method @NonNull public android.service.credentials.CredentialsResponseContent build();
method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setActions(@NonNull java.util.List<android.service.credentials.Action>);
method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setCredentialEntries(@NonNull java.util.List<android.service.credentials.CredentialEntry>);
method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.CredentialEntry);
}
public final class GetCredentialsRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public String getCallingPackage();
method @NonNull public java.util.List<android.credentials.GetCredentialOption> getGetCredentialOptions();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.GetCredentialsRequest> CREATOR;
}
public static final class GetCredentialsRequest.Builder {
ctor public GetCredentialsRequest.Builder(@NonNull String);
method @NonNull public android.service.credentials.GetCredentialsRequest.Builder addGetCredentialOption(@NonNull android.credentials.GetCredentialOption);
method @NonNull public android.service.credentials.GetCredentialsRequest build();
method @NonNull public android.service.credentials.GetCredentialsRequest.Builder setGetCredentialOptions(@NonNull java.util.List<android.credentials.GetCredentialOption>);
}
public final class GetCredentialsResponse implements android.os.Parcelable {
method @NonNull public static android.service.credentials.GetCredentialsResponse createWithAuthentication(@NonNull android.service.credentials.Action);
method @NonNull public static android.service.credentials.GetCredentialsResponse createWithResponseContent(@NonNull android.service.credentials.CredentialsResponseContent);
method public int describeContents();
method @Nullable public android.service.credentials.Action getAuthenticationAction();
method @Nullable public android.service.credentials.CredentialsResponseContent getCredentialsResponseContent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.GetCredentialsResponse> CREATOR;
}
}
package android.service.dreams {
public class DreamService extends android.app.Service implements android.view.Window.Callback {

View File

@@ -27,8 +27,6 @@ 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 {
/** Slice object containing display content to be displayed with this action on the UI. */
@@ -39,6 +37,13 @@ public final class Action implements Parcelable {
/**
* Constructs an action to be displayed on the UI.
*
* <p> Actions must be used for any provider related operations, such as opening the provider
* app, intenting straight into certain app activities like 'manage credentials', top
* level authentication before displaying any content etc.
*
* <p> See details on usage of {@code Action} for various actionable entries in
* {@link BeginCreateCredentialResponse} and {@link GetCredentialsResponse}.
*
* @param slice the display content to be displayed on the UI, along with this action
* @param pendingIntent the intent to be invoked when the user selects this action
*/

View File

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

View File

@@ -0,0 +1,102 @@
/*
* 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 java.util.Objects;
/**
* Request for beginning a create credential request.
*
* See {@link BeginCreateCredentialResponse} for the counterpart response
*/
public final class BeginCreateCredentialRequest implements Parcelable {
private final @NonNull String mCallingPackage;
private final @NonNull String mType;
private final @NonNull Bundle mData;
/**
* Constructs a new instance.
*
* @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
* null or empty.
* @throws NullPointerException If {@code data} is null.
*/
public BeginCreateCredentialRequest(@NonNull String callingPackage,
@NonNull String type, @NonNull Bundle data) {
mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
"callingPackage must not be null or empty");
mType = Preconditions.checkStringNotEmpty(type,
"type must not be null or empty");
mData = Objects.requireNonNull(data, "data must not be null");
}
private BeginCreateCredentialRequest(@NonNull Parcel in) {
mCallingPackage = in.readString8();
mType = in.readString8();
mData = in.readBundle(Bundle.class.getClassLoader());
}
public static final @NonNull Creator<BeginCreateCredentialRequest> CREATOR =
new Creator<BeginCreateCredentialRequest>() {
@Override
public BeginCreateCredentialRequest createFromParcel(@NonNull Parcel in) {
return new BeginCreateCredentialRequest(in);
}
@Override
public BeginCreateCredentialRequest[] newArray(int size) {
return new BeginCreateCredentialRequest[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mCallingPackage);
dest.writeString8(mType);
dest.writeBundle(mData);
}
/** Returns the calling package of the calling app. */
@NonNull
public String getCallingPackage() {
return mCallingPackage;
}
/** Returns the type of the credential to be created. */
@NonNull
public String getType() {
return mType;
}
/** Returns the data to be used while resolving the credential to create. */
@NonNull
public Bundle getData() {
return mData;
}
}

View File

@@ -16,4 +16,4 @@
package android.service.credentials;
parcelable CreateCredentialResponse;
parcelable BeginCreateCredentialResponse;

View File

@@ -0,0 +1,150 @@
/*
* 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.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Response to a {@link BeginCreateCredentialRequest}.
*/
public final class BeginCreateCredentialResponse implements Parcelable {
private final @NonNull List<CreateEntry> mCreateEntries;
private final @Nullable CreateEntry mRemoteCreateEntry;
private BeginCreateCredentialResponse(@NonNull Parcel in) {
List<CreateEntry> createEntries = new ArrayList<>();
in.readTypedList(createEntries, CreateEntry.CREATOR);
mCreateEntries = createEntries;
mRemoteCreateEntry = in.readTypedObject(CreateEntry.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedList(mCreateEntries);
dest.writeTypedObject(mRemoteCreateEntry, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final @NonNull Creator<BeginCreateCredentialResponse> CREATOR =
new Creator<BeginCreateCredentialResponse>() {
@Override
public BeginCreateCredentialResponse createFromParcel(@NonNull Parcel in) {
return new BeginCreateCredentialResponse(in);
}
@Override
public BeginCreateCredentialResponse[] newArray(int size) {
return new BeginCreateCredentialResponse[size];
}
};
/* package-private */ BeginCreateCredentialResponse(
@NonNull List<CreateEntry> createEntries,
@Nullable CreateEntry remoteCreateEntry) {
this.mCreateEntries = createEntries;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mCreateEntries);
this.mRemoteCreateEntry = remoteCreateEntry;
}
/** Returns the list of create entries to be displayed on the UI. */
public @NonNull List<CreateEntry> getCreateEntries() {
return mCreateEntries;
}
/** Returns the remote create entry to be displayed on the UI. */
public @Nullable CreateEntry getRemoteCreateEntry() {
return mRemoteCreateEntry;
}
/**
* A builder for {@link BeginCreateCredentialResponse}
*/
@SuppressWarnings("WeakerAccess") /* synthetic access */
public static final class Builder {
private @NonNull List<CreateEntry> mCreateEntries = new ArrayList<>();
private @Nullable CreateEntry mRemoteCreateEntry;
/**
* Sets the list of create entries to be shown on the UI.
*
* @throws IllegalArgumentException If {@code createEntries} is empty.
* @throws NullPointerException If {@code createEntries} is null, or any of its elements
* are null.
*/
public @NonNull Builder setCreateEntries(@NonNull List<CreateEntry> createEntries) {
Preconditions.checkCollectionNotEmpty(createEntries, "createEntries");
mCreateEntries = Preconditions.checkCollectionElementsNotNull(
createEntries, "createEntries");
return this;
}
/**
* Adds an entry to the list of create entries to be shown on the UI.
*
* @throws NullPointerException If {@code createEntry} is null.
*/
public @NonNull Builder addCreateEntry(@NonNull CreateEntry createEntry) {
mCreateEntries.add(Objects.requireNonNull(createEntry));
return this;
}
/**
* Sets a remote create entry to be shown on the UI. Provider must set this entry if they
* wish to create the credential on a different device.
*
* <p> When constructing the {@link CreateEntry} object, the {@code pendingIntent} must be
* set such that it leads to an activity that can provide UI to fulfill the request on
* a remote device. When user selects this {@code remoteCreateEntry}, the system will
* invoke the {@code pendingIntent} set on the {@link CreateEntry}.
*
* <p> Once the remote credential flow is complete, the {@link android.app.Activity}
* result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the
* {@link CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESULT} key should be populated
* with a {@link android.credentials.CreateCredentialResponse} object.
*/
public @NonNull Builder setRemoteCreateEntry(@Nullable CreateEntry remoteCreateEntry) {
mRemoteCreateEntry = remoteCreateEntry;
return this;
}
/**
* Builds a new instance of {@link BeginCreateCredentialResponse}.
*
* @throws NullPointerException If {@code createEntries} is null.
* @throws IllegalArgumentException If {@code createEntries} is empty.
*/
public @NonNull BeginCreateCredentialResponse build() {
Preconditions.checkCollectionNotEmpty(mCreateEntries, "createEntries must "
+ "not be null, or empty");
return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry);
}
}
}

View File

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

View File

@@ -27,8 +27,6 @@ import java.util.Objects;
/**
* Request for creating a credential.
*
* @hide
*/
public final class CreateCredentialRequest implements Parcelable {
private final @NonNull String mCallingPackage;

View File

@@ -1,143 +0,0 @@
/*
* 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.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Response to a {@link CreateCredentialRequest}.
*
* @hide
*/
public final class CreateCredentialResponse implements Parcelable {
private final @NonNull List<SaveEntry> mSaveEntries;
private final @Nullable Action mRemoteSaveEntry;
//TODO : Add actions if needed
private CreateCredentialResponse(@NonNull Parcel in) {
List<SaveEntry> saveEntries = new ArrayList<>();
in.readTypedList(saveEntries, SaveEntry.CREATOR);
mSaveEntries = saveEntries;
mRemoteSaveEntry = in.readTypedObject(Action.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedList(mSaveEntries);
dest.writeTypedObject(mRemoteSaveEntry, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final @NonNull Creator<CreateCredentialResponse> CREATOR =
new Creator<CreateCredentialResponse>() {
@Override
public CreateCredentialResponse createFromParcel(@NonNull Parcel in) {
return new CreateCredentialResponse(in);
}
@Override
public CreateCredentialResponse[] newArray(int size) {
return new CreateCredentialResponse[size];
}
};
/* package-private */ CreateCredentialResponse(
@NonNull List<SaveEntry> saveEntries,
@Nullable Action remoteSaveEntry) {
this.mSaveEntries = saveEntries;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSaveEntries);
this.mRemoteSaveEntry = remoteSaveEntry;
}
/** Returns the list of save entries to be displayed on the UI. */
public @NonNull List<SaveEntry> getSaveEntries() {
return mSaveEntries;
}
/** Returns the remote save entry to be displayed on the UI. */
public @NonNull Action getRemoteSaveEntry() {
return mRemoteSaveEntry;
}
/**
* A builder for {@link CreateCredentialResponse}
*/
@SuppressWarnings("WeakerAccess")
public static final class Builder {
private @NonNull List<SaveEntry> mSaveEntries = new ArrayList<>();
private @Nullable Action mRemoteSaveEntry;
/**
* Sets the list of save entries to be shown on the UI.
*
* @throws IllegalArgumentException If {@code saveEntries} is empty.
* @throws NullPointerException If {@code saveEntries} is null, or any of its elements
* are null.
*/
public @NonNull Builder setSaveEntries(@NonNull List<SaveEntry> saveEntries) {
Preconditions.checkCollectionNotEmpty(saveEntries, "saveEntries");
mSaveEntries = Preconditions.checkCollectionElementsNotNull(
saveEntries, "saveEntries");
return this;
}
/**
* Adds an entry to the list of save entries to be shown on the UI.
*
* @throws NullPointerException If {@code saveEntry} is null.
*/
public @NonNull Builder addSaveEntry(@NonNull SaveEntry saveEntry) {
mSaveEntries.add(Objects.requireNonNull(saveEntry));
return this;
}
/**
* Sets a remote save entry to be shown on the UI.
*/
public @NonNull Builder setRemoteSaveEntry(@Nullable Action remoteSaveEntry) {
mRemoteSaveEntry = remoteSaveEntry;
return this;
}
/**
* Builds the instance.
*
* @throws IllegalArgumentException If {@code saveEntries} is empty.
*/
public @NonNull CreateCredentialResponse build() {
Preconditions.checkCollectionNotEmpty(mSaveEntries, "saveEntries must "
+ "not be empty");
return new CreateCredentialResponse(
mSaveEntries,
mRemoteSaveEntry);
}
}
}

View File

@@ -25,27 +25,25 @@ import android.os.Parcelable;
/**
* An entry to be shown on the UI. This entry represents where the credential to be created will
* be stored. Examples include user's account, family group etc.
*
* @hide
*/
public final class SaveEntry implements Parcelable {
public final class CreateEntry implements Parcelable {
private final @NonNull Slice mSlice;
private final @NonNull PendingIntent mPendingIntent;
private SaveEntry(@NonNull Parcel in) {
private CreateEntry(@NonNull Parcel in) {
mSlice = in.readTypedObject(Slice.CREATOR);
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
}
public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() {
public static final @NonNull Creator<CreateEntry> CREATOR = new Creator<CreateEntry>() {
@Override
public SaveEntry createFromParcel(@NonNull Parcel in) {
return new SaveEntry(in);
public CreateEntry createFromParcel(@NonNull Parcel in) {
return new CreateEntry(in);
}
@Override
public SaveEntry[] newArray(int size) {
return new SaveEntry[size];
public CreateEntry[] newArray(int size) {
return new CreateEntry[size];
}
};
@@ -61,12 +59,12 @@ public final class SaveEntry implements Parcelable {
}
/**
* Constructs a save entry to be displayed on the UI.
* Constructs a CreateEntry to be displayed on the UI.
*
* @param slice the display content to be displayed on the UI, along with this entry
* @param pendingIntent the intent to be invoked when the user selects this entry
*/
public SaveEntry(
public CreateEntry(
@NonNull Slice slice,
@NonNull PendingIntent pendingIntent) {
this.mSlice = slice;
@@ -77,12 +75,12 @@ public final class SaveEntry implements Parcelable {
NonNull.class, null, mPendingIntent);
}
/** Returns the content to be displayed with this save entry on the UI. */
/** Returns the content to be displayed with this create entry on the UI. */
public @NonNull Slice getSlice() {
return mSlice;
}
/** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
/** Returns the pendingIntent to be invoked when this create entry on the UI is selectcd. */
public @NonNull PendingIntent getPendingIntent() {
return mPendingIntent;
}

View File

@@ -31,8 +31,6 @@ 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. */
@@ -145,61 +143,67 @@ public final class CredentialEntry implements Parcelable {
private boolean mAutoSelectAllowed = false;
/**
* Builds the instance.
* Creates a builder for a {@link CredentialEntry} that should invoke a
* {@link PendingIntent} when selected by the user.
*
* <p>The {@code pendingIntent} can be used to launch activities that require some user
* engagement before getting the credential corresponding to this entry,
* e.g. authentication, confirmation etc.
* Once the activity fulfills the required user engagement, the
* {@link android.app.Activity} result should be set to
* {@link android.app.Activity#RESULT_OK}, and the
* {@link CredentialProviderService#EXTRA_CREDENTIAL_RESULT} must be set with a
* {@link Credential} object.
*
* @param type the type of credential underlying this credential entry
* @param slice the content to be displayed with this entry on the UI
* @param pendingIntent the pendingIntent to be invoked when this entry is selected by the
* user
*
* @throws IllegalArgumentException If {@code type} is null or empty.
* @throws NullPointerException If {@code slice} is null.
* @throws NullPointerException If {@code slice}, or {@code pendingIntent} is null.
* @throws IllegalArgumentException If {@code type} is null or empty, or if
* {@code pendingIntent} was not created with {@link PendingIntent#getActivity}
* or {@link PendingIntent#getActivities}.
*/
public Builder(@NonNull String type, @NonNull Slice slice) {
public Builder(@NonNull String type, @NonNull Slice slice,
@NonNull PendingIntent pendingIntent) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ "null, or empty");
mSlice = Objects.requireNonNull(slice,
"slice must not be null");
mPendingIntent = Objects.requireNonNull(pendingIntent,
"pendingIntent must not be null");
if (!mPendingIntent.isActivity()) {
throw new IllegalStateException("Pending intent must start an activity");
}
}
/**
* Sets the pendingIntent to be invoked if the user selects this entry.
* Creates a builder for a {@link CredentialEntry} that contains a {@link Credential},
* and does not require further action.
* @param type the type of credential underlying this credential entry
* @param slice the content to be displayed with this entry on the UI
* @param credential the credential to be returned to the client app, when this entry is
* selected by the user
*
* The pending intent can be used to launch activities that require some user engagement
* before getting the credential corresponding to this entry, e.g. authentication,
* confirmation etc.
* Once the activity fulfills the required user engagement, a {@link Credential} object
* must be returned as an extra on activity finish.
*
* @throws IllegalStateException If {@code credential} is already set. Must either set the
* {@code credential}, or the {@code pendingIntent}.
* @throws IllegalArgumentException If {@code type} is null or empty.
* @throws NullPointerException If {@code slice}, or {@code credential} is null.
*/
public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
if (pendingIntent != null) {
Preconditions.checkState(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) {
if (credential != null) {
Preconditions.checkState(mPendingIntent == null,
"pendingIntent is already set. Cannot set both the "
+ "pendingIntent and the credential");
}
mCredential = credential;
return this;
public Builder(@NonNull String type, @NonNull Slice slice, @NonNull Credential credential) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ "null, or empty");
mSlice = Objects.requireNonNull(slice,
"slice must not be null");
mCredential = Objects.requireNonNull(credential,
"credential must not be null");
}
/**
* Sets whether the entry is allowed to be auto selected by the framework.
* The default value is set to false.
*
* <p> The entry is only auto selected if it is the only entry on the user selector,
* AND the developer has also enabled auto select, while building the request.
*/
public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) {
mAutoSelectAllowed = autoSelectAllowed;

View File

@@ -24,8 +24,6 @@ import java.lang.annotation.RetentionPolicy;
/**
* Contains custom exceptions to be used by credential providers on failure.
*
* @hide
*/
public class CredentialProviderException extends Exception {
public static final int ERROR_UNKNOWN = 0;
@@ -59,6 +57,11 @@ public class CredentialProviderException extends Exception {
@Retention(RetentionPolicy.SOURCE)
public @interface CredentialProviderError { }
public CredentialProviderException(@CredentialProviderError int errorCode,
@NonNull String message, @NonNull Throwable cause) {
super(message, cause);
mErrorCode = errorCode;
}
public CredentialProviderException(@CredentialProviderError int errorCode,
@NonNull String message) {

View File

@@ -37,30 +37,63 @@ import java.util.Objects;
/**
* Service to be extended by credential providers, in order to return user credentials
* to the framework.
*
* @hide
*/
public abstract class CredentialProviderService extends Service {
/** Extra to be used by provider to populate the credential when ending the activity started
* through the {@code pendingIntent} on the selected {@link SaveEntry}. **/
public static final String EXTRA_CREATE_CREDENTIAL_RESPONSE =
"android.service.credentials.extra.CREATE_CREDENTIAL_RESPONSE";
/** Extra to be used by provider to populate the {@link CredentialsDisplayContent} when
* an authentication action entry is selected. **/
public static final String EXTRA_GET_CREDENTIALS_DISPLAY_CONTENT =
"android.service.credentials.extra.GET_CREDENTIALS_DISPLAY_CONTENT";
/**
* Intent extra: The {@link android.credentials.CreateCredentialRequest} attached with
* the {@code pendingIntent} that is invoked when the user selects a {@link CreateEntry}
* returned as part of the {@link BeginCreateCredentialResponse}
*
* <p>
* Type: {@link android.credentials.CreateCredentialRequest}
*/
public static final String EXTRA_CREATE_CREDENTIAL_REQUEST =
"android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST";
/**
* Provider must read the value against this extra to receive the complete create credential
* request parameters, when a pending intent is launched.
* Intent extra: The result of a create flow operation, to be set on finish of the
* {@link android.app.Activity} invoked through the {@code pendingIntent} set on
* a {@link CreateEntry}.
*
* <p>
* Type: {@link android.credentials.CreateCredentialResponse}
*/
public static final String EXTRA_CREATE_CREDENTIAL_REQUEST_PARAMS =
"android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST_PARAMS";
public static final String EXTRA_CREATE_CREDENTIAL_RESULT =
"android.service.credentials.extra.CREATE_CREDENTIAL_RESULT";
/** Extra to be used by the provider when setting the credential result. */
public static final String EXTRA_GET_CREDENTIAL =
"android.service.credentials.extra.GET_CREDENTIAL";
/**
* Intent extra: The result of a get credential flow operation, to be set on finish of the
* {@link android.app.Activity} invoked through the {@code pendingIntent} set on
* a {@link CredentialEntry}.
*
* <p>
* Type: {@link android.credentials.Credential}
*/
public static final String EXTRA_CREDENTIAL_RESULT =
"android.service.credentials.extra.CREDENTIAL_RESULT";
/**
* Intent extra: The result of an authentication flow, to be set on finish of the
* {@link android.app.Activity} invoked through the {@link android.app.PendingIntent} set on
* a {@link GetCredentialsResponse}. This result should contain the actual content, including
* credential entries and action entries, to be shown on the selector.
*
* <p>
* Type: {@link CredentialsResponseContent}
*/
public static final String EXTRA_GET_CREDENTIALS_CONTENT_RESULT =
"android.service.credentials.extra.GET_CREDENTIALS_CONTENT_RESULT";
/**
* Intent extra: The error result of any {@link android.app.PendingIntent} flow, to be set
* on finish of the corresponding {@link android.app.Activity}. This result should contain an
* error code, representing the error encountered by the provider.
*
* <p>
* Type: {@link String}
*/
public static final String EXTRA_ERROR =
"android.service.credentials.extra.ERROR";
private static final String TAG = "CredProviderService";
@@ -129,20 +162,21 @@ public abstract class CredentialProviderService extends Service {
}
@Override
public ICancellationSignal onCreateCredential(CreateCredentialRequest request,
ICreateCredentialCallback callback) {
public ICancellationSignal onBeginCreateCredential(BeginCreateCredentialRequest request,
IBeginCreateCredentialCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(callback);
ICancellationSignal transport = CancellationSignal.createTransport();
mHandler.sendMessage(obtainMessage(
CredentialProviderService::onCreateCredential,
CredentialProviderService::onBeginCreateCredential,
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
new OutcomeReceiver<CreateCredentialResponse, CredentialProviderException>() {
new OutcomeReceiver<
BeginCreateCredentialResponse, CredentialProviderException>() {
@Override
public void onResult(CreateCredentialResponse result) {
public void onResult(BeginCreateCredentialResponse result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -182,8 +216,8 @@ public abstract class CredentialProviderService extends Service {
* the android system.
* @param callback Object used to relay the response of the credential creation request.
*/
public abstract void onCreateCredential(@NonNull CreateCredentialRequest request,
public abstract void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver<CreateCredentialResponse,
@NonNull OutcomeReceiver<BeginCreateCredentialResponse,
CredentialProviderException> callback);
}

View File

@@ -28,12 +28,10 @@ import java.util.List;
import java.util.Objects;
/**
* Content to be displayed on the account selector UI, including credential entries,
* actions etc.
*
* @hide
* The content to be displayed on the account selector UI, including credential entries,
* actions etc. Returned as part of {@link GetCredentialsResponse}
*/
public final class CredentialsDisplayContent implements Parcelable {
public final class CredentialsResponseContent implements Parcelable {
/** List of credential entries to be displayed on the UI. */
private final @NonNull List<CredentialEntry> mCredentialEntries;
@@ -41,36 +39,36 @@ public final class CredentialsDisplayContent implements Parcelable {
private final @NonNull List<Action> mActions;
/** Remote credential entry to get the response from a different device. */
private final @Nullable Action mRemoteCredentialEntry;
private final @Nullable CredentialEntry mRemoteCredentialEntry;
private CredentialsDisplayContent(@NonNull List<CredentialEntry> credentialEntries,
private CredentialsResponseContent(@NonNull List<CredentialEntry> credentialEntries,
@NonNull List<Action> actions,
@Nullable Action remoteCredentialEntry) {
@Nullable CredentialEntry remoteCredentialEntry) {
mCredentialEntries = credentialEntries;
mActions = actions;
mRemoteCredentialEntry = remoteCredentialEntry;
}
private CredentialsDisplayContent(@NonNull Parcel in) {
private CredentialsResponseContent(@NonNull Parcel in) {
List<CredentialEntry> credentialEntries = new ArrayList<>();
in.readTypedList(credentialEntries, CredentialEntry.CREATOR);
mCredentialEntries = credentialEntries;
List<Action> actions = new ArrayList<>();
in.readTypedList(actions, Action.CREATOR);
mActions = actions;
mRemoteCredentialEntry = in.readTypedObject(Action.CREATOR);
mRemoteCredentialEntry = in.readTypedObject(CredentialEntry.CREATOR);
}
public static final @NonNull Creator<CredentialsDisplayContent> CREATOR =
new Creator<CredentialsDisplayContent>() {
public static final @NonNull Creator<CredentialsResponseContent> CREATOR =
new Creator<CredentialsResponseContent>() {
@Override
public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) {
return new CredentialsDisplayContent(in);
public CredentialsResponseContent createFromParcel(@NonNull Parcel in) {
return new CredentialsResponseContent(in);
}
@Override
public CredentialsDisplayContent[] newArray(int size) {
return new CredentialsDisplayContent[size];
public CredentialsResponseContent[] newArray(int size) {
return new CredentialsResponseContent[size];
}
};
@@ -103,22 +101,34 @@ public final class CredentialsDisplayContent implements Parcelable {
/**
* Returns the remote credential entry to be displayed on the UI.
*/
public @Nullable Action getRemoteCredentialEntry() {
public @Nullable CredentialEntry getRemoteCredentialEntry() {
return mRemoteCredentialEntry;
}
/**
* Builds an instance of {@link CredentialsDisplayContent}.
* Builds an instance of {@link CredentialsResponseContent}.
*/
public static final class Builder {
private List<CredentialEntry> mCredentialEntries = new ArrayList<>();
private List<Action> mActions = new ArrayList<>();
private Action mRemoteCredentialEntry;
private CredentialEntry mRemoteCredentialEntry;
/**
* Sets the remote credential entry to be displayed on the UI.
* Sets a remote credential entry to be shown on the UI. Provider must set this if they
* wish to get the credential from a different device.
*
* <p> When constructing the {@link CredentialEntry} object, the {@code pendingIntent}
* must be set such that it leads to an activity that can provide UI to fulfill the request
* on a remote device. When user selects this {@code remoteCredentialEntry}, the system will
* invoke the {@code pendingIntent} set on the {@link CredentialEntry}.
*
* <p> Once the remote credential flow is complete, the {@link android.app.Activity}
* result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the
* {@link CredentialProviderService#EXTRA_CREDENTIAL_RESULT} key should be populated
* with a {@link android.credentials.Credential} object.
*/
public @NonNull Builder setRemoteCredentialEntry(@Nullable Action remoteCredentialEntry) {
public @NonNull Builder setRemoteCredentialEntry(@Nullable CredentialEntry
remoteCredentialEntry) {
mRemoteCredentialEntry = remoteCredentialEntry;
return this;
}
@@ -138,6 +148,11 @@ public final class CredentialsDisplayContent implements Parcelable {
* Adds an {@link Action} to the list of actions to be displayed on
* the UI.
*
* <p> An {@code action} must be used for independent user actions,
* such as opening the app, intenting directly into a certain app activity etc. The
* {@code pendingIntent} set with the {@code action} must invoke the corresponding
* activity.
*
* @throws NullPointerException If {@code action} is null.
*/
public @NonNull Builder addAction(@NonNull Action action) {
@@ -175,17 +190,16 @@ public final class CredentialsDisplayContent implements Parcelable {
/**
* Builds a {@link GetCredentialsResponse} instance.
*
* @throws NullPointerException If {@code credentialEntries} is null.
* @throws IllegalStateException if both {@code credentialEntries} and
* {@code actions} are empty.
* @throws IllegalStateException if {@code credentialEntries}, {@code actions}
* and {@code remoteCredentialEntry} are all null or empty.
*/
public @NonNull CredentialsDisplayContent build() {
public @NonNull CredentialsResponseContent build() {
if (mCredentialEntries != null && mCredentialEntries.isEmpty()
&& mActions != null && mActions.isEmpty()) {
&& mActions != null && mActions.isEmpty() && mRemoteCredentialEntry == null) {
throw new IllegalStateException("credentialEntries and actions must not both "
+ "be empty");
}
return new CredentialsDisplayContent(mCredentialEntries, mActions,
return new CredentialsResponseContent(mCredentialEntries, mActions,
mRemoteCredentialEntry);
}
}

View File

@@ -29,8 +29,6 @@ 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. */

View File

@@ -26,12 +26,10 @@ 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;
private final @Nullable CredentialsResponseContent mCredentialsResponseContent;
/**
* Authentication action that must be launched and completed before showing any content
@@ -40,11 +38,17 @@ public final class GetCredentialsResponse implements Parcelable {
private final @Nullable Action mAuthenticationAction;
/**
* Creates a {@link GetCredentialsRequest} instance with an authentication action set.
* Creates a {@link GetCredentialsResponse} instance with an authentication {@link Action} set.
* Providers must use this method when no content can be shown before authentication.
*
* Once the authentication action activity is launched, and the user is authenticated, providers
* should create another response with {@link CredentialsDisplayContent} using
* <p> When the user selects this {@code authenticationAction}, the system invokes the
* corresponding {@code pendingIntent}. Once the authentication flow is complete,
* the {@link android.app.Activity} result should be set
* to {@link android.app.Activity#RESULT_OK} and the
* {@link CredentialProviderService#EXTRA_GET_CREDENTIALS_CONTENT_RESULT} extra should be set
* with a fully populated {@link CredentialsResponseContent} object.
* the authentication action activity is launched, and the user is authenticated, providers
* should create another response with {@link CredentialsResponseContent} using
* {@code createWithDisplayContent}, and add that response to the result of the authentication
* activity.
*
@@ -58,27 +62,27 @@ public final class GetCredentialsResponse implements Parcelable {
}
/**
* Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI.
* Creates a {@link GetCredentialsRequest} instance with content to be shown on the UI.
* Providers must use this method when there is content to be shown without top level
* authentication required.
* authentication required, including credential entries, action entries or a remote entry,
*
* @throws NullPointerException If {@code credentialsDisplayContent} is null.
* @throws NullPointerException If {@code credentialsResponseContent} is null.
*/
public static @NonNull GetCredentialsResponse createWithDisplayContent(
@NonNull CredentialsDisplayContent credentialsDisplayContent) {
Objects.requireNonNull(credentialsDisplayContent,
"credentialsDisplayContent must not be null");
return new GetCredentialsResponse(credentialsDisplayContent, null);
public static @NonNull GetCredentialsResponse createWithResponseContent(
@NonNull CredentialsResponseContent credentialsResponseContent) {
Objects.requireNonNull(credentialsResponseContent,
"credentialsResponseContent must not be null");
return new GetCredentialsResponse(credentialsResponseContent, null);
}
private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent,
private GetCredentialsResponse(@Nullable CredentialsResponseContent credentialsResponseContent,
@Nullable Action authenticationAction) {
mCredentialsDisplayContent = credentialsDisplayContent;
mCredentialsResponseContent = credentialsResponseContent;
mAuthenticationAction = authenticationAction;
}
private GetCredentialsResponse(@NonNull Parcel in) {
mCredentialsDisplayContent = in.readTypedObject(CredentialsDisplayContent.CREATOR);
mCredentialsResponseContent = in.readTypedObject(CredentialsResponseContent.CREATOR);
mAuthenticationAction = in.readTypedObject(Action.CREATOR);
}
@@ -102,23 +106,23 @@ public final class GetCredentialsResponse implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedObject(mCredentialsDisplayContent, flags);
dest.writeTypedObject(mCredentialsResponseContent, flags);
dest.writeTypedObject(mAuthenticationAction, flags);
}
/**
* Returns the authentication action to be invoked before any other content
* can be shown to the user.
* If this response represents a top level authentication action, returns the authentication
* action to be invoked before any other content can be shown to the user.
*/
public @Nullable Action getAuthenticationAction() {
return mAuthenticationAction;
}
/**
* Returns the credentialDisplayContent that does not require authentication, and
* can be shown to the user on the account selector UI.
* Returns the actual content to be displayed on the selector, if this response does not
* require any top level authentication.
*/
public @Nullable CredentialsDisplayContent getCredentialsDisplayContent() {
return mCredentialsDisplayContent;
public @Nullable CredentialsResponseContent getCredentialsResponseContent() {
return mCredentialsResponseContent;
}
}

View File

@@ -1,13 +1,13 @@
package android.service.credentials;
import android.service.credentials.CreateCredentialResponse;
import android.service.credentials.BeginCreateCredentialResponse;
/**
* Interface from the system to a credential provider service.
*
* @hide
*/
oneway interface ICreateCredentialCallback {
void onSuccess(in CreateCredentialResponse request);
oneway interface IBeginCreateCredentialCallback {
void onSuccess(in BeginCreateCredentialResponse request);
void onFailure(int errorCode, in CharSequence message);
}

View File

@@ -18,9 +18,9 @@ package android.service.credentials;
import android.os.ICancellationSignal;
import android.service.credentials.GetCredentialsRequest;
import android.service.credentials.CreateCredentialRequest;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.IGetCredentialsCallback;
import android.service.credentials.ICreateCredentialCallback;
import android.service.credentials.IBeginCreateCredentialCallback;
import android.os.ICancellationSignal;
/**
@@ -30,5 +30,5 @@ import android.os.ICancellationSignal;
*/
interface ICredentialProviderService {
ICancellationSignal onGetCredentials(in GetCredentialsRequest request, in IGetCredentialsCallback callback);
ICancellationSignal onCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback);
ICancellationSignal onBeginCreateCredential(in BeginCreateCredentialRequest request, in IBeginCreateCredentialCallback callback);
}

View File

@@ -39,6 +39,7 @@ import android.graphics.drawable.Icon
import android.os.Binder
import android.os.Bundle
import android.os.ResultReceiver
import android.service.credentials.CredentialProviderService
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateScreenState
@@ -390,11 +391,12 @@ class CredentialManagerRepo(
intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
or PendingIntent.FLAG_ONE_SHOT))
val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
context.applicationInfo.packageName,
"PASSWORD",
toBundle("beckett-bakert@gmail.com", "password123")
context.applicationInfo.packageName,
TYPE_PASSWORD_CREDENTIAL,
toBundle("beckett-bakert@gmail.com", "password123")
)
val fillInIntent = Intent().putExtra("create_request_params", createPasswordRequest)
val fillInIntent = Intent().putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
createPasswordRequest)
val slice = Slice.Builder(
Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)

View File

@@ -35,6 +35,7 @@ import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.GetCredentialsRequest;
import android.text.TextUtils;
import android.util.Log;
@@ -199,7 +200,7 @@ public final class CredentialManagerService extends
// Iterate over all provider sessions and invoke the request
providerSessions.forEach(providerCreateSession -> {
providerCreateSession.getRemoteCredentialService().onCreateCredential(
(android.service.credentials.CreateCredentialRequest)
(BeginCreateCredentialRequest)
providerCreateSession.getProviderRequest(),
/*callback=*/providerCreateSession);
});

View File

@@ -22,7 +22,7 @@ import android.credentials.CreateCredentialResponse;
import android.credentials.Credential;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.CredentialsDisplayContent;
import android.service.credentials.CredentialsResponseContent;
/**
* Helper class for setting up pending intent, and extracting objects from it.
@@ -37,14 +37,15 @@ public class PendingIntentResultHandler {
return pendingIntentResponse.getResultCode() == Activity.RESULT_OK;
}
/** Extracts the {@link CredentialsDisplayContent} object added to the result data. */
public static CredentialsDisplayContent extractCredentialsDisplayContent(Intent resultData) {
/** Extracts the {@link CredentialsResponseContent} object added to the result data. */
public static CredentialsResponseContent extractResponseContent(Intent resultData) {
if (resultData == null) {
return null;
}
return resultData.getParcelableExtra(
CredentialProviderService.EXTRA_GET_CREDENTIALS_DISPLAY_CONTENT,
CredentialsDisplayContent.class);
CredentialProviderService
.EXTRA_GET_CREDENTIALS_CONTENT_RESULT,
CredentialsResponseContent.class);
}
/** Extracts the {@link CreateCredentialResponse} object added to the result data. */
@@ -53,7 +54,7 @@ public class PendingIntentResultHandler {
return null;
}
return resultData.getParcelableExtra(
CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESPONSE,
CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESULT,
CreateCredentialResponse.class);
}
@@ -63,7 +64,7 @@ public class PendingIntentResultHandler {
return null;
}
return resultData.getParcelableExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL,
CredentialProviderService.EXTRA_CREDENTIAL_RESULT,
Credential.class);
}
}

View File

@@ -26,11 +26,11 @@ import android.credentials.ui.CreateCredentialProviderData;
import android.credentials.ui.Entry;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.os.Bundle;
import android.service.credentials.CreateCredentialRequest;
import android.service.credentials.CreateCredentialResponse;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.BeginCreateCredentialResponse;
import android.service.credentials.CreateEntry;
import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.SaveEntry;
import android.util.Log;
import android.util.Slog;
@@ -44,16 +44,16 @@ import java.util.Map;
* Will likely split this into remote response state and UI state.
*/
public final class ProviderCreateSession extends ProviderSession<
CreateCredentialRequest, CreateCredentialResponse> {
BeginCreateCredentialRequest, BeginCreateCredentialResponse> {
private static final String TAG = "ProviderCreateSession";
// Key to be used as an entry key for a save entry
private static final String SAVE_ENTRY_KEY = "save_entry_key";
@NonNull
private final Map<String, SaveEntry> mUiSaveEntries = new HashMap<>();
private final Map<String, CreateEntry> mUiSaveEntries = new HashMap<>();
/** The complete request to be used in the second round. */
private final CreateCredentialRequest mCompleteRequest;
private final BeginCreateCredentialRequest mCompleteRequest;
/** Creates a new provider session to be used by the request session. */
@Nullable public static ProviderCreateSession createNewSession(
@@ -62,7 +62,7 @@ public final class ProviderCreateSession extends ProviderSession<
CredentialProviderInfo providerInfo,
CreateRequestSession createRequestSession,
RemoteCredentialService remoteCredentialService) {
CreateCredentialRequest providerRequest =
BeginCreateCredentialRequest providerRequest =
createProviderRequest(providerInfo.getCapabilities(),
createRequestSession.mClientRequest,
createRequestSession.mClientCallingPackage);
@@ -75,21 +75,23 @@ public final class ProviderCreateSession extends ProviderSession<
}
@Nullable
private static CreateCredentialRequest createProviderRequest(List<String> providerCapabilities,
private static BeginCreateCredentialRequest createProviderRequest(
List<String> providerCapabilities,
android.credentials.CreateCredentialRequest clientRequest,
String clientCallingPackage) {
String capability = clientRequest.getType();
if (providerCapabilities.contains(capability)) {
return new CreateCredentialRequest(clientCallingPackage, capability,
return new BeginCreateCredentialRequest(clientCallingPackage, capability,
clientRequest.getData());
}
Log.i(TAG, "Unable to create provider request - capabilities do not match");
return null;
}
private static CreateCredentialRequest getFirstRoundRequest(CreateCredentialRequest request) {
private static BeginCreateCredentialRequest getFirstRoundRequest(
BeginCreateCredentialRequest request) {
// TODO: Replace with first round bundle from request when ready
return new CreateCredentialRequest(
return new BeginCreateCredentialRequest(
request.getCallingPackage(),
request.getType(),
new Bundle());
@@ -101,7 +103,7 @@ public final class ProviderCreateSession extends ProviderSession<
@NonNull ProviderInternalCallback callbacks,
@UserIdInt int userId,
@NonNull RemoteCredentialService remoteCredentialService,
@NonNull CreateCredentialRequest request) {
@NonNull BeginCreateCredentialRequest request) {
super(context, info, getFirstRoundRequest(request), callbacks, userId,
remoteCredentialService);
// TODO : Replace with proper splitting of request
@@ -110,13 +112,13 @@ public final class ProviderCreateSession extends ProviderSession<
}
/** Returns the save entry maintained in state by this provider session. */
public SaveEntry getUiSaveEntry(String entryId) {
public CreateEntry getUiSaveEntry(String entryId) {
return mUiSaveEntries.get(entryId);
}
@Override
public void onProviderResponseSuccess(
@Nullable CreateCredentialResponse response) {
@Nullable BeginCreateCredentialResponse response) {
Log.i(TAG, "in onProviderResponseSuccess");
onUpdateResponse(response);
}
@@ -138,7 +140,7 @@ public final class ProviderCreateSession extends ProviderSession<
}
}
private void onUpdateResponse(CreateCredentialResponse response) {
private void onUpdateResponse(BeginCreateCredentialResponse response) {
Log.i(TAG, "updateResponse with save entries");
mProviderResponse = response;
updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED);
@@ -152,15 +154,15 @@ public final class ProviderCreateSession extends ProviderSession<
Log.i(TAG, "In prepareUiData not in uiInvokingStatus");
return null;
}
final CreateCredentialResponse response = getProviderResponse();
final BeginCreateCredentialResponse response = getProviderResponse();
if (response == null) {
Log.i(TAG, "In prepareUiData response null");
throw new IllegalStateException("Response must be in completion mode");
}
if (response.getSaveEntries() != null) {
if (response.getCreateEntries() != null) {
Log.i(TAG, "In prepareUiData save entries not null");
return prepareUiProviderData(
prepareUiSaveEntries(response.getSaveEntries()),
prepareUiSaveEntries(response.getCreateEntries()),
null,
/*isDefaultProvider=*/false);
}
@@ -192,24 +194,25 @@ public final class ProviderCreateSession extends ProviderSession<
}
}
private List<Entry> prepareUiSaveEntries(@NonNull List<SaveEntry> saveEntries) {
private List<Entry> prepareUiSaveEntries(@NonNull List<CreateEntry> saveEntries) {
Log.i(TAG, "in populateUiSaveEntries");
List<Entry> uiSaveEntries = new ArrayList<>();
// Populate the save entries
for (SaveEntry saveEntry : saveEntries) {
for (CreateEntry createEntry : saveEntries) {
String entryId = generateEntryId();
mUiSaveEntries.put(entryId, saveEntry);
mUiSaveEntries.put(entryId, createEntry);
Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, saveEntry.getSlice(),
saveEntry.getPendingIntent(), setUpFillInIntent(saveEntry.getPendingIntent())));
uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, createEntry.getSlice(),
createEntry.getPendingIntent(), setUpFillInIntent(
createEntry.getPendingIntent())));
}
return uiSaveEntries;
}
private Intent setUpFillInIntent(PendingIntent pendingIntent) {
Intent intent = pendingIntent.getIntent();
intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST_PARAMS,
intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
mCompleteRequest);
return intent;
}

View File

@@ -29,7 +29,7 @@ import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.Action;
import android.service.credentials.CredentialEntry;
import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialsDisplayContent;
import android.service.credentials.CredentialsResponseContent;
import android.service.credentials.GetCredentialsRequest;
import android.service.credentials.GetCredentialsResponse;
import android.util.Log;
@@ -211,20 +211,20 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ
prepareUiAuthenticationAction(mProviderResponse.getAuthenticationAction()),
/*remoteEntry=*/null);
}
if (mProviderResponse.getCredentialsDisplayContent() != null) {
Log.i(TAG, "In prepareUiData displayContent not null");
if (mProviderResponse.getCredentialsResponseContent() != null) {
Log.i(TAG, "In prepareUiData credentialsResponseContent not null");
return prepareUiProviderData(prepareUiActionEntries(
mProviderResponse.getCredentialsDisplayContent().getActions()),
prepareUiCredentialEntries(mProviderResponse.getCredentialsDisplayContent()
mProviderResponse.getCredentialsResponseContent().getActions()),
prepareUiCredentialEntries(mProviderResponse.getCredentialsResponseContent()
.getCredentialEntries()),
/*authenticationAction=*/null,
prepareUiRemoteEntry(mProviderResponse
.getCredentialsDisplayContent().getRemoteCredentialEntry()));
.getCredentialsResponseContent().getRemoteCredentialEntry()));
}
return null;
}
private Entry prepareUiRemoteEntry(Action remoteCredentialEntry) {
private Entry prepareUiRemoteEntry(CredentialEntry remoteCredentialEntry) {
if (remoteCredentialEntry == null) {
return null;
}
@@ -316,11 +316,11 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ
@Nullable ProviderPendingIntentResponse providerPendingIntentResponse) {
if (providerPendingIntentResponse != null) {
if (PendingIntentResultHandler.isSuccessfulResponse(providerPendingIntentResponse)) {
CredentialsDisplayContent content = PendingIntentResultHandler
.extractCredentialsDisplayContent(providerPendingIntentResponse
CredentialsResponseContent content = PendingIntentResultHandler
.extractResponseContent(providerPendingIntentResponse
.getResultData());
if (content != null) {
onUpdateResponse(GetCredentialsResponse.createWithDisplayContent(content));
onUpdateResponse(GetCredentialsResponse.createWithResponseContent(content));
return;
}
}
@@ -342,7 +342,7 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ
if (response.getAuthenticationAction() != null) {
Log.i(TAG , "updateResponse with authentication entry");
updateStatusAndInvokeCallback(Status.REQUIRES_AUTHENTICATION);
} else if (response.getCredentialsDisplayContent() != null) {
} else if (response.getCredentialsResponseContent() != null) {
Log.i(TAG , "updateResponse with credentialEntries");
// TODO validate response
updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);

View File

@@ -23,7 +23,7 @@ import android.content.Context;
import android.credentials.Credential;
import android.credentials.ui.ProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.Action;
import android.service.credentials.CredentialEntry;
import android.service.credentials.CredentialProviderException;
import android.service.credentials.CredentialProviderInfo;
import android.util.Pair;
@@ -50,7 +50,7 @@ public abstract class ProviderSession<T, R>
@Nullable protected Credential mFinalCredentialResponse;
@NonNull protected final T mProviderRequest;
@Nullable protected R mProviderResponse;
@Nullable protected Pair<String, Action> mUiRemoteEntry;
@Nullable protected Pair<String, CredentialEntry> mUiRemoteEntry;
/**
* Returns true if the given status reflects that the provider state is ready to be shown

View File

@@ -24,14 +24,14 @@ import android.content.Intent;
import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CreateCredentialRequest;
import android.service.credentials.CreateCredentialResponse;
import android.service.credentials.BeginCreateCredentialRequest;
import android.service.credentials.BeginCreateCredentialResponse;
import android.service.credentials.CredentialProviderException;
import android.service.credentials.CredentialProviderException.CredentialProviderError;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialsRequest;
import android.service.credentials.GetCredentialsResponse;
import android.service.credentials.ICreateCredentialCallback;
import android.service.credentials.IBeginCreateCredentialCallback;
import android.service.credentials.ICredentialProviderService;
import android.service.credentials.IGetCredentialsCallback;
import android.text.format.DateUtils;
@@ -146,27 +146,27 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
handleExecutionResponse(result, error, cancellationSink, callback)));
}
/** Main entry point to be called for executing a createCredential call on the remote
/** Main entry point to be called for executing a beginCreateCredential call on the remote
* provider service.
* @param request the request to be sent to the provider
* @param callback the callback to be used to send back the provider response to the
* {@link ProviderCreateSession} class that maintains provider state
*/
public void onCreateCredential(@NonNull CreateCredentialRequest request,
ProviderCallbacks<CreateCredentialResponse> callback) {
public void onCreateCredential(@NonNull BeginCreateCredentialRequest request,
ProviderCallbacks<BeginCreateCredentialResponse> callback) {
Log.i(TAG, "In onCreateCredential in RemoteCredentialService");
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
AtomicReference<CompletableFuture<CreateCredentialResponse>> futureRef =
AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef =
new AtomicReference<>();
CompletableFuture<CreateCredentialResponse> connectThenExecute = postAsync(service -> {
CompletableFuture<CreateCredentialResponse> createCredentialFuture =
CompletableFuture<BeginCreateCredentialResponse> connectThenExecute = postAsync(service -> {
CompletableFuture<BeginCreateCredentialResponse> createCredentialFuture =
new CompletableFuture<>();
ICancellationSignal cancellationSignal = service.onCreateCredential(
request, new ICreateCredentialCallback.Stub() {
ICancellationSignal cancellationSignal = service.onBeginCreateCredential(
request, new IBeginCreateCredentialCallback.Stub() {
@Override
public void onSuccess(CreateCredentialResponse response) {
Log.i(TAG, "In onSuccess onCreateCredential "
public void onSuccess(BeginCreateCredentialResponse response) {
Log.i(TAG, "In onSuccess onBeginCreateCredential "
+ "in RemoteCredentialService");
createCredentialFuture.complete(response);
}
@@ -179,7 +179,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
createCredentialFuture.completeExceptionally(
new CredentialProviderException(errorCode, errorMsg));
}});
CompletableFuture<CreateCredentialResponse> future = futureRef.get();
CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get();
if (future != null && future.isCancelled()) {
dispatchCancellationSignal(cancellationSignal);
} else {