diff --git a/core/api/current.txt b/core/api/current.txt index d4c461cd93846..4b0c96f25eec9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -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 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 CREATOR; + } + + public final class BeginCreateCredentialResponse implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List 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 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); + 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 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 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 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); + 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); + 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 getActions(); + method @NonNull public java.util.List 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 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); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setCredentialEntries(@NonNull java.util.List); + 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 getGetCredentialOptions(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator 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); + } + + 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 CREATOR; + } + +} + package android.service.dreams { public class DreamService extends android.app.Service implements android.view.Window.Callback { diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java index 553a32419533f..77570813e6c32 100644 --- a/core/java/android/service/credentials/Action.java +++ b/core/java/android/service/credentials/Action.java @@ -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. * + *

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. + * + *

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 */ diff --git a/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl b/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl new file mode 100644 index 0000000000000..30cab8df443b2 --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl @@ -0,0 +1,3 @@ +package android.service.credentials; + +parcelable BeginCreateCredentialRequest; \ No newline at end of file diff --git a/core/java/android/service/credentials/BeginCreateCredentialRequest.java b/core/java/android/service/credentials/BeginCreateCredentialRequest.java new file mode 100644 index 0000000000000..1918d8c88cccd --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialRequest.java @@ -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 CREATOR = + new Creator() { + @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; + } +} diff --git a/core/java/android/service/credentials/CreateCredentialResponse.aidl b/core/java/android/service/credentials/BeginCreateCredentialResponse.aidl similarity index 94% rename from core/java/android/service/credentials/CreateCredentialResponse.aidl rename to core/java/android/service/credentials/BeginCreateCredentialResponse.aidl index 73c9147b0ae42..d2a1408f06813 100644 --- a/core/java/android/service/credentials/CreateCredentialResponse.aidl +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.aidl @@ -16,4 +16,4 @@ package android.service.credentials; -parcelable CreateCredentialResponse; +parcelable BeginCreateCredentialResponse; diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java new file mode 100644 index 0000000000000..022678ea49bd4 --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java @@ -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 mCreateEntries; + private final @Nullable CreateEntry mRemoteCreateEntry; + + private BeginCreateCredentialResponse(@NonNull Parcel in) { + List 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 CREATOR = + new Creator() { + @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 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 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 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 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. + * + *

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}. + * + *

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); + } + } +} diff --git a/core/java/android/service/credentials/CreateCredentialRequest.aidl b/core/java/android/service/credentials/CreateCredentialRequest.aidl deleted file mode 100644 index eb7fba9405f7b..0000000000000 --- a/core/java/android/service/credentials/CreateCredentialRequest.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.service.credentials; - -parcelable CreateCredentialRequest; \ No newline at end of file diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java index e6da349a2fbe7..aee85abcc8b80 100644 --- a/core/java/android/service/credentials/CreateCredentialRequest.java +++ b/core/java/android/service/credentials/CreateCredentialRequest.java @@ -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; diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java deleted file mode 100644 index f69dca81950df..0000000000000 --- a/core/java/android/service/credentials/CreateCredentialResponse.java +++ /dev/null @@ -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 mSaveEntries; - private final @Nullable Action mRemoteSaveEntry; - //TODO : Add actions if needed - - private CreateCredentialResponse(@NonNull Parcel in) { - List 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 CREATOR = - new Creator() { - @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 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 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 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 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); - } - } -} diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/CreateEntry.java similarity index 77% rename from core/java/android/service/credentials/SaveEntry.java rename to core/java/android/service/credentials/CreateEntry.java index 55ff6ff9755f0..eb25e25a725a2 100644 --- a/core/java/android/service/credentials/SaveEntry.java +++ b/core/java/android/service/credentials/CreateEntry.java @@ -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 CREATOR = new Creator() { + public static final @NonNull Creator CREATOR = new Creator() { @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; } diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java index 98c537a6d6ef1..941db02be8d1e 100644 --- a/core/java/android/service/credentials/CredentialEntry.java +++ b/core/java/android/service/credentials/CredentialEntry.java @@ -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. + * + *

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. + * + *

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; diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java index 06f0052a29a9d..02b74430493cd 100644 --- a/core/java/android/service/credentials/CredentialProviderException.java +++ b/core/java/android/service/credentials/CredentialProviderException.java @@ -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) { diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index 24b7c3c439d4d..32646e6fd2891 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -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} + * + *

+ * 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}. + * + *

+ * 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}. + * + *

+ * 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. + * + *

+ * 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. + * + *

+ * 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() { + 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 callback); } diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsResponseContent.java similarity index 64% rename from core/java/android/service/credentials/CredentialsDisplayContent.java rename to core/java/android/service/credentials/CredentialsResponseContent.java index 4b23800891a85..32cab5004ac01 100644 --- a/core/java/android/service/credentials/CredentialsDisplayContent.java +++ b/core/java/android/service/credentials/CredentialsResponseContent.java @@ -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 mCredentialEntries; @@ -41,36 +39,36 @@ public final class CredentialsDisplayContent implements Parcelable { private final @NonNull List 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 credentialEntries, + private CredentialsResponseContent(@NonNull List credentialEntries, @NonNull List actions, - @Nullable Action remoteCredentialEntry) { + @Nullable CredentialEntry remoteCredentialEntry) { mCredentialEntries = credentialEntries; mActions = actions; mRemoteCredentialEntry = remoteCredentialEntry; } - private CredentialsDisplayContent(@NonNull Parcel in) { + private CredentialsResponseContent(@NonNull Parcel in) { List credentialEntries = new ArrayList<>(); in.readTypedList(credentialEntries, CredentialEntry.CREATOR); mCredentialEntries = credentialEntries; List 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 CREATOR = - new Creator() { + public static final @NonNull Creator CREATOR = + new Creator() { @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 mCredentialEntries = new ArrayList<>(); private List 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. + * + *

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}. + * + *

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. * + *

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); } } diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java index 03ba20e1df276..9052b54c82914 100644 --- a/core/java/android/service/credentials/GetCredentialsRequest.java +++ b/core/java/android/service/credentials/GetCredentialsRequest.java @@ -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. */ diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java index 979a6993c3d49..5263141f982a5 100644 --- a/core/java/android/service/credentials/GetCredentialsResponse.java +++ b/core/java/android/service/credentials/GetCredentialsResponse.java @@ -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 + *

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; } } diff --git a/core/java/android/service/credentials/ICreateCredentialCallback.aidl b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl similarity index 50% rename from core/java/android/service/credentials/ICreateCredentialCallback.aidl rename to core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl index 4cc76a4a341b1..ec0bc364a4577 100644 --- a/core/java/android/service/credentials/ICreateCredentialCallback.aidl +++ b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl @@ -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); } \ No newline at end of file diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl index c21cefab701ad..b9eb3ed9571a4 100644 --- a/core/java/android/service/credentials/ICredentialProviderService.aidl +++ b/core/java/android/service/credentials/ICredentialProviderService.aidl @@ -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); } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 2bede9acaed21..94bd0573e0230 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -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) diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 30fcd06327dd5..d3b9e10a436ab 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -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); }); diff --git a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java index 4cdc4570ba90a..d0bc0744f8a5a 100644 --- a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java +++ b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java @@ -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); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 6bb8c60ec06fa..14fee78baacbb 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -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 mUiSaveEntries = new HashMap<>(); + private final Map 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 providerCapabilities, + private static BeginCreateCredentialRequest createProviderRequest( + List 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 prepareUiSaveEntries(@NonNull List saveEntries) { + private List prepareUiSaveEntries(@NonNull List saveEntries) { Log.i(TAG, "in populateUiSaveEntries"); List 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; } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index d63cdebe0e1b3..6cd011b7a686d 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -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 @Nullable protected Credential mFinalCredentialResponse; @NonNull protected final T mProviderRequest; @Nullable protected R mProviderResponse; - @Nullable protected Pair mUiRemoteEntry; + @Nullable protected Pair mUiRemoteEntry; /** * Returns true if the given status reflects that the provider state is ready to be shown diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java index c2464b5d235eb..e385bcb32201b 100644 --- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java +++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java @@ -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 callback) { + public void onCreateCredential(@NonNull BeginCreateCredentialRequest request, + ProviderCallbacks callback) { Log.i(TAG, "In onCreateCredential in RemoteCredentialService"); AtomicReference cancellationSink = new AtomicReference<>(); - AtomicReference> futureRef = + AtomicReference> futureRef = new AtomicReference<>(); - CompletableFuture connectThenExecute = postAsync(service -> { - CompletableFuture createCredentialFuture = + CompletableFuture connectThenExecute = postAsync(service -> { + CompletableFuture 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 future = futureRef.get(); + CompletableFuture future = futureRef.get(); if (future != null && future.isCancelled()) { dispatchCancellationSignal(cancellationSignal); } else {