diff options
19 files changed, 581 insertions, 292 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 86f2b6281254..35d0f6478cc9 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4390,6 +4390,59 @@ package android.content.rollback { package android.credentials.selection { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class AuthenticationEntry implements android.os.Parcelable { + ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int, @NonNull android.content.Intent); + method public int describeContents(); + method @Nullable public android.content.Intent getFrameworkExtrasIntent(); + method @NonNull public String getKey(); + method @NonNull public android.app.slice.Slice getSlice(); + method @NonNull public int getStatus(); + method @NonNull public String getSubkey(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.AuthenticationEntry> CREATOR; + field public static final int STATUS_LOCKED = 0; // 0x0 + field public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1; // 0x1 + field public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2; // 0x2 + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getAppPackageName(); + method @NonNull public android.credentials.selection.RequestToken getRequestToken(); + method public boolean shouldShowCancellationExplanation(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.CancelSelectionRequest> CREATOR; + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderInfo { + method @NonNull public String getProviderName(); + method @Nullable public android.credentials.selection.Entry getRemoteEntry(); + method @NonNull public java.util.List<android.credentials.selection.Entry> getSaveEntries(); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public static final class CreateCredentialProviderInfo.Builder { + ctor public CreateCredentialProviderInfo.Builder(@NonNull String); + method @NonNull public android.credentials.selection.CreateCredentialProviderInfo build(); + method @NonNull public android.credentials.selection.CreateCredentialProviderInfo.Builder setRemoteEntry(@Nullable android.credentials.selection.Entry); + method @NonNull public android.credentials.selection.CreateCredentialProviderInfo.Builder setSaveEntries(@NonNull java.util.List<android.credentials.selection.Entry>); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class DisabledProviderInfo { + ctor public DisabledProviderInfo(@NonNull String); + method @NonNull public String getProviderName(); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class Entry implements android.os.Parcelable { + ctor public Entry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, @NonNull android.content.Intent); + method public int describeContents(); + method @Nullable public android.content.Intent getFrameworkExtrasIntent(); + method @NonNull public String getKey(); + method @NonNull public android.app.slice.Slice getSlice(); + method @NonNull public String getSubkey(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.Entry> CREATOR; + } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class FailureResult { ctor public FailureResult(int, @Nullable String); method public int getErrorCode(); @@ -4399,6 +4452,32 @@ package android.credentials.selection { field public static final int ERROR_CODE_UI_FAILURE = 0; // 0x0 } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class GetCredentialProviderInfo { + method @NonNull public java.util.List<android.credentials.selection.Entry> getActionChips(); + method @NonNull public java.util.List<android.credentials.selection.AuthenticationEntry> getAuthenticationEntries(); + method @NonNull public java.util.List<android.credentials.selection.Entry> getCredentialEntries(); + method @NonNull public String getProviderName(); + method @Nullable public android.credentials.selection.Entry getRemoteEntry(); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public static final class GetCredentialProviderInfo.Builder { + ctor public GetCredentialProviderInfo.Builder(@NonNull String); + method @NonNull public android.credentials.selection.GetCredentialProviderInfo build(); + method @NonNull public android.credentials.selection.GetCredentialProviderInfo.Builder setActionChips(@NonNull java.util.List<android.credentials.selection.Entry>); + method @NonNull public android.credentials.selection.GetCredentialProviderInfo.Builder setAuthenticationEntries(@NonNull java.util.List<android.credentials.selection.AuthenticationEntry>); + method @NonNull public android.credentials.selection.GetCredentialProviderInfo.Builder setCredentialEntries(@NonNull java.util.List<android.credentials.selection.Entry>); + method @NonNull public android.credentials.selection.GetCredentialProviderInfo.Builder setRemoteEntry(@Nullable android.credentials.selection.Entry); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class IntentHelper { + method @Nullable public static android.credentials.selection.CancelSelectionRequest extractCancelUiRequest(@NonNull android.content.Intent); + method @NonNull public static java.util.List<android.credentials.selection.CreateCredentialProviderInfo> extractCreateCredentialProviderInfoList(@NonNull android.content.Intent); + method @NonNull public static java.util.List<android.credentials.selection.DisabledProviderInfo> extractDisabledProviderInfoList(@NonNull android.content.Intent); + method @NonNull public static java.util.List<android.credentials.selection.GetCredentialProviderInfo> extractGetCredentialProviderInfoList(@NonNull android.content.Intent); + method @Nullable public static android.credentials.selection.RequestInfo extractRequestInfo(@NonNull android.content.Intent); + method @Nullable public static android.os.ResultReceiver extractResultReceiver(@NonNull android.content.Intent); + } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class ProviderPendingIntentResponse implements android.os.Parcelable { ctor public ProviderPendingIntentResponse(int, @Nullable android.content.Intent); method public int describeContents(); @@ -4408,6 +4487,26 @@ package android.credentials.selection { field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.ProviderPendingIntentResponse> CREATOR; } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getAppPackageName(); + method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest(); + method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds(); + method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest(); + method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds(); + method @NonNull public android.credentials.selection.RequestToken getRequestToken(); + method @NonNull public String getType(); + method public boolean hasPermissionToOverrideDefault(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.RequestInfo> CREATOR; + field @NonNull public static final String TYPE_CREATE = "android.credentials.selection.TYPE_CREATE"; + field @NonNull public static final String TYPE_GET = "android.credentials.selection.TYPE_GET"; + field @NonNull public static final String TYPE_UNDEFINED = "android.credentials.selection.TYPE_UNDEFINED"; + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestToken { + } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class ResultHelper { method public static void sendFailureResult(@NonNull android.os.ResultReceiver, @NonNull android.credentials.selection.FailureResult); method public static void sendUserSelectionResult(@NonNull android.os.ResultReceiver, @NonNull android.credentials.selection.UserSelectionResult); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 77add41f6805..ddb2532b76f7 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1267,22 +1267,6 @@ package android.credentials { package android.credentials.selection { - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class AuthenticationEntry implements android.os.Parcelable { - ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int); - ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int, @NonNull android.content.Intent); - method public int describeContents(); - method @Nullable public android.content.Intent getFrameworkExtrasIntent(); - method @NonNull public String getKey(); - method @NonNull public android.app.slice.Slice getSlice(); - method @NonNull public int getStatus(); - method @NonNull public String getSubkey(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.AuthenticationEntry> CREATOR; - field public static final int STATUS_LOCKED = 0; // 0x0 - field public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1; // 0x1 - field public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2; // 0x2 - } - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class BaseDialogResult implements android.os.Parcelable { ctor public BaseDialogResult(@Nullable android.os.IBinder); ctor protected BaseDialogResult(@NonNull android.os.Parcel); @@ -1298,6 +1282,10 @@ package android.credentials.selection { field public static final int RESULT_CODE_DIALOG_USER_CANCELED = 0; // 0x0 } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable { + ctor @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public CancelSelectionRequest(@NonNull android.os.IBinder, boolean, @NonNull String); + } + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable { ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @Nullable android.credentials.selection.Entry); method @Nullable public android.credentials.selection.Entry getRemoteEntry(); @@ -1317,19 +1305,6 @@ package android.credentials.selection { field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.DisabledProviderData> CREATOR; } - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class Entry implements android.os.Parcelable { - ctor public Entry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice); - ctor public Entry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, @NonNull android.content.Intent); - method public int describeContents(); - method @Nullable public android.content.Intent getFrameworkExtrasIntent(); - method @NonNull public String getKey(); - method @Nullable public android.app.PendingIntent getPendingIntent(); - method @NonNull public android.app.slice.Slice getSlice(); - method @NonNull public String getSubkey(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.Entry> CREATOR; - } - @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class FailureDialogResult extends android.credentials.selection.BaseDialogResult implements android.os.Parcelable { ctor public FailureDialogResult(@Nullable android.os.IBinder, @Nullable String); method public static void addToBundle(@NonNull android.credentials.selection.FailureDialogResult, @NonNull android.os.Bundle); @@ -1357,6 +1332,7 @@ package android.credentials.selection { } @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory { + method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.os.IBinder, boolean, @NonNull String); method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver); } @@ -1371,25 +1347,12 @@ package android.credentials.selection { } @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public String getAppPackageName(); - method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest(); - method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds(); - method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest(); - method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds(); - method @NonNull public android.os.IBinder getToken(); - method @NonNull public String getType(); - method public boolean hasPermissionToOverrideDefault(); - method @NonNull public static android.credentials.selection.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String); - method @NonNull public static android.credentials.selection.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String, boolean, @NonNull java.util.List<java.lang.String>); - method @NonNull public static android.credentials.selection.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String, boolean); - method @NonNull public static android.credentials.selection.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.RequestInfo> CREATOR; - field @NonNull public static final String EXTRA_REQUEST_INFO = "android.credentials.selection.extra.REQUEST_INFO"; - field @NonNull public static final String TYPE_CREATE = "android.credentials.selection.TYPE_CREATE"; - field @NonNull public static final String TYPE_GET = "android.credentials.selection.TYPE_GET"; - field @NonNull public static final String TYPE_UNDEFINED = "android.credentials.selection.TYPE_UNDEFINED"; + method @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") @NonNull public static android.credentials.selection.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String, boolean, @NonNull java.util.List<java.lang.String>); + method @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") @NonNull public static android.credentials.selection.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String, boolean); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestToken { + ctor @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public RequestToken(@NonNull android.os.IBinder); } @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class UserSelectionDialogResult extends android.credentials.selection.BaseDialogResult implements android.os.Parcelable { diff --git a/core/java/android/credentials/selection/AuthenticationEntry.java b/core/java/android/credentials/selection/AuthenticationEntry.java index 54589e1aed8b..dd6ca9e8ec51 100644 --- a/core/java/android/credentials/selection/AuthenticationEntry.java +++ b/core/java/android/credentials/selection/AuthenticationEntry.java @@ -23,9 +23,13 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.annotation.TestApi; +import android.annotation.SystemApi; import android.app.slice.Slice; +import android.content.Context; import android.content.Intent; +import android.credentials.GetCredentialRequest; +import android.os.CancellationSignal; +import android.os.OutcomeReceiver; import android.os.Parcel; import android.os.Parcelable; @@ -33,17 +37,20 @@ import com.android.internal.util.AnnotationValidations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * An authentication entry. * - * Applicable only for credential retrieval flow, authentication entries are a special type of - * entries that require the user to unlock the given provider before its credential options can - * be fully rendered. + * Applicable only for + * {@link android.credentials.CredentialManager#getCredential(Context, GetCredentialRequest, + * CancellationSignal, Executor, OutcomeReceiver)} flow, authentication entries are a special type + * of entries that require the user to unlock the given provider before its credential options + * can be fully rendered. * * @hide */ -@TestApi +@SystemApi @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class AuthenticationEntry implements Parcelable { @NonNull @@ -67,17 +74,31 @@ public final class AuthenticationEntry implements Parcelable { public @interface Status { } - /** This entry is still locked, as initially supplied by the provider. */ + /** + * This entry is still locked, as initially supplied by the provider. + * + * This entry should be rendered in a way to signal that it is still locked, and when chosen + * will lead to an unlock challenge (e.g. draw a trailing lock icon on this entry). + */ public static final int STATUS_LOCKED = 0; /** * This entry was unlocked but didn't contain any credential. Meanwhile, "less recent" means * there is another such entry that was unlocked more recently. + * + * This entry should be rendered in a way to signal that it was unlocked but turns out to + * contain no credential that can be used, and as a result, it should be unclickable. */ public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1; /** * This is the most recent entry that was unlocked but didn't contain any credential. * * There will be at most one authentication entry with this status. + * + * This entry should be rendered in a way to signal that it was unlocked but turns out to + * contain no credential that can be used, and as a result, it should be unclickable. + * + * If this was the last clickable option prior to unlocking, then the UI should display an + * information that all options are exhausted then gracefully finish itself. */ public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2; @@ -94,29 +115,36 @@ public final class AuthenticationEntry implements Parcelable { } /** - * Constructor to be used for an entry that does not require further activities - * to be invoked when selected. + * Constructor to be used for an entry that requires a pending intent to be invoked + * when clicked. + * + * @param key the identifier of this entry that's unique within the context of the given + * CredentialManager request. This is used when constructing the + * {@link android.credentials.selection.UserSelectionResult#UserSelectionResult( + * String providerId, String entryKey, String entrySubkey, + * ProviderPendingIntentResponse providerPendingIntentResponse)} + * + * @param subkey the sub-identifier of this entry that's unique within the context of the + * {@code key}. This is used when constructing the + * {@link android.credentials.selection.UserSelectionResult#UserSelectionResult( + * String providerId, String entryKey, String entrySubkey, + * ProviderPendingIntentResponse providerPendingIntentResponse)} + * @param intent the intent containing extra data that has to be filled in when launching this + * entry's provider PendingIntent + * @param slice the Slice to be displayed + * @param status the entry status, depending on which the entry should be rendered differently */ - // TODO(b/322065508): remove this constructor. public AuthenticationEntry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice, - @Status int status) { + @Status int status, @NonNull Intent intent) { mKey = key; mSubkey = subkey; mSlice = slice; mStatus = status; - } - - /** Constructor to be used for an entry that requires a pending intent to be invoked - * when clicked. - */ - public AuthenticationEntry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice, - @Status int status, @NonNull Intent intent) { - this(key, subkey, slice, status); mFrameworkExtrasIntent = intent; } /** - * Returns the identifier of this entry that's unique within the context of the + * Returns the identifier of this entry that's unique within the context of the given * CredentialManager request. */ @NonNull @@ -138,7 +166,7 @@ public final class AuthenticationEntry implements Parcelable { return mSlice; } - /** Returns the entry status, depending on which the entry will be rendered differently. */ + /** Returns the entry status, depending on which the entry should be rendered differently. */ @NonNull @Status public int getStatus() { @@ -146,8 +174,10 @@ public final class AuthenticationEntry implements Parcelable { } /** - * Returns the framework intent to be filled in when launching this entry's provider - * PendingIntent. + * Returns the intent containing extra data that has to be filled in when launching this + * entry's provider PendingIntent. + * + * If null, the provider PendingIntent can be launched without any fill in intent. */ @Nullable @SuppressLint("IntentBuilderName") // Not building a new intent. diff --git a/core/java/android/credentials/selection/CancelSelectionRequest.java b/core/java/android/credentials/selection/CancelSelectionRequest.java new file mode 100644 index 000000000000..2662d761c1c0 --- /dev/null +++ b/core/java/android/credentials/selection/CancelSelectionRequest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.credentials.selection; + +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; + +/** + * A request to cancel the ongoing selection UI matching the identifier token in this request. + * + * Upon receiving this request, the UI should gracefully finish itself if the given request token + * {@link CancelSelectionRequest#getToken()} matches that of the selection UI is currently rendered + * for. Also, the UI should display some informational cancellation message (e.g. "Request is + * cancelled by the app") before closing when the + * {@link CancelSelectionRequest#shouldShowCancellationExplanation()} is true. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) +public final class CancelSelectionRequest implements Parcelable { + + /** + * The intent extra key for the {@code CancelUiRequest} object when launching the UX + * activities. + * + * @hide + */ + @NonNull + public static final String EXTRA_CANCEL_UI_REQUEST = + "android.credentials.selection.extra.CANCEL_UI_REQUEST"; + + @NonNull + private final IBinder mToken; + + private final boolean mShouldShowCancellationExplanation; + + @NonNull + private final String mAppPackageName; + + /** + * Returns the request token matching the user request that should be cancelled. + * + * The request token for the current UI can be found from the UI launch intent, mapping to + * {@link RequestInfo#getToken()}. + * + * @hide + */ + @NonNull + public IBinder getToken() { + return mToken; + } + + /** Returns the request token matching the app request that should be cancelled. */ + @NonNull + public RequestToken getRequestToken() { + return new RequestToken(mToken); + } + + /** + * Returns the app package name invoking this request, that can be used to derive display + * metadata (e.g. "Cancelled by `App Name`"). + */ + @NonNull + public String getAppPackageName() { + return mAppPackageName; + } + + /** + * Returns whether the UI should display some informational cancellation message (e.g. + * "Request is cancelled by the app") before closing. If false, the UI should be silently + * cancelled. + */ + public boolean shouldShowCancellationExplanation() { + return mShouldShowCancellationExplanation; + } + + /** + * Constructs a {@link CancelSelectionRequest}. + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) + public CancelSelectionRequest(@NonNull IBinder token, boolean shouldShowCancellationExplanation, + @NonNull String appPackageName) { + mToken = token; + mShouldShowCancellationExplanation = shouldShowCancellationExplanation; + mAppPackageName = appPackageName; + } + + private CancelSelectionRequest(@NonNull Parcel in) { + mToken = in.readStrongBinder(); + AnnotationValidations.validate(NonNull.class, null, mToken); + mShouldShowCancellationExplanation = in.readBoolean(); + mAppPackageName = in.readString8(); + AnnotationValidations.validate(NonNull.class, null, mAppPackageName); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeBoolean(mShouldShowCancellationExplanation); + dest.writeString8(mAppPackageName); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<CancelSelectionRequest> CREATOR = new Creator<>() { + @Override + public CancelSelectionRequest createFromParcel(@NonNull Parcel in) { + return new CancelSelectionRequest(in); + } + + @Override + public CancelSelectionRequest[] newArray(int size) { + return new CancelSelectionRequest[size]; + } + }; +} diff --git a/core/java/android/credentials/selection/CancelUiRequest.java b/core/java/android/credentials/selection/CancelUiRequest.java deleted file mode 100644 index fca0e2ad184e..000000000000 --- a/core/java/android/credentials/selection/CancelUiRequest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.credentials.selection; - -import android.annotation.NonNull; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.util.AnnotationValidations; - -/** - * A request to cancel the ongoing UI matching the identifier token in this request. - * - * @hide - */ -public final class CancelUiRequest implements Parcelable { - - /** - * The intent extra key for the {@code CancelUiRequest} object when launching the UX - * activities. - * - * @hide - */ - @NonNull - public static final String EXTRA_CANCEL_UI_REQUEST = - "android.credentials.selection.extra.CANCEL_UI_REQUEST"; - - @NonNull - private final IBinder mToken; - - private final boolean mShouldShowCancellationUi; - - @NonNull - private final String mAppPackageName; - - /** Returns the request token matching the user request that should be cancelled. */ - @NonNull - public IBinder getToken() { - return mToken; - } - - /** - * Returns the app package name invoking this request, that can be used to derive display - * metadata (e.g. "Cancelled by `App Name`"). - */ - @NonNull - public String getAppPackageName() { - return mAppPackageName; - } - - /** - * Returns whether the UI should render a cancellation UI upon the request. If false, the UI - * will be silently cancelled. - */ - public boolean shouldShowCancellationUi() { - return mShouldShowCancellationUi; - } - - /** Constructs a {@link CancelUiRequest}. */ - public CancelUiRequest(@NonNull IBinder token, boolean shouldShowCancellationUi, - @NonNull String appPackageName) { - mToken = token; - mShouldShowCancellationUi = shouldShowCancellationUi; - mAppPackageName = appPackageName; - } - - private CancelUiRequest(@NonNull Parcel in) { - mToken = in.readStrongBinder(); - AnnotationValidations.validate(NonNull.class, null, mToken); - mShouldShowCancellationUi = in.readBoolean(); - mAppPackageName = in.readString8(); - AnnotationValidations.validate(NonNull.class, null, mAppPackageName); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeStrongBinder(mToken); - dest.writeBoolean(mShouldShowCancellationUi); - dest.writeString8(mAppPackageName); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - public static final Creator<CancelUiRequest> CREATOR = new Creator<>() { - @Override - public CancelUiRequest createFromParcel(@NonNull Parcel in) { - return new CancelUiRequest(in); - } - - @Override - public CancelUiRequest[] newArray(int size) { - return new CancelUiRequest[size]; - } - }; -} diff --git a/core/java/android/credentials/selection/CreateCredentialProviderData.java b/core/java/android/credentials/selection/CreateCredentialProviderData.java index fc80ea8cec8a..ba9b00a13a82 100644 --- a/core/java/android/credentials/selection/CreateCredentialProviderData.java +++ b/core/java/android/credentials/selection/CreateCredentialProviderData.java @@ -118,9 +118,12 @@ public final class CreateCredentialProviderData extends ProviderData implements @TestApi @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public static final class Builder { - @NonNull private String mProviderFlattenedComponentName; - @NonNull private List<Entry> mSaveEntries = new ArrayList<>(); - @Nullable private Entry mRemoteEntry = null; + @NonNull + private String mProviderFlattenedComponentName; + @NonNull + private List<Entry> mSaveEntries = new ArrayList<>(); + @Nullable + private Entry mRemoteEntry = null; /** Constructor with required properties. */ public Builder(@NonNull String providerFlattenedComponentName) { diff --git a/core/java/android/credentials/selection/CreateCredentialProviderInfo.java b/core/java/android/credentials/selection/CreateCredentialProviderInfo.java index 78b9fd445f49..6d02f0d036bb 100644 --- a/core/java/android/credentials/selection/CreateCredentialProviderInfo.java +++ b/core/java/android/credentials/selection/CreateCredentialProviderInfo.java @@ -16,21 +16,40 @@ package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; +import android.credentials.CreateCredentialRequest; +import android.os.CancellationSignal; +import android.os.OutcomeReceiver; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** - * Information pertaining to a specific provider during the given create-credential flow. + * Per-provider metadata and entries for the + * {@link android.credentials.CredentialManager#createCredential(Context, CreateCredentialRequest, + * CancellationSignal, Executor, OutcomeReceiver)} flow. * * This includes provider metadata and its credential creation options for display purposes. * + * The selection UI should render all options (from + * {@link CreateCredentialProviderInfo#getSaveEntries()} and + * {@link CreateCredentialProviderInfo#getRemoteEntry()}) offered by this provider while clearly + * associating them with the given provider using the provider icon, label, etc. derived from + * {@link CreateCredentialProviderInfo#getProviderName()}. + * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class CreateCredentialProviderInfo { @NonNull @@ -65,7 +84,8 @@ public final class CreateCredentialProviderInfo { * Returns the remote credential saving option, if any. * * Notice that only one system configured provider can set this option, and when set, it means - * that the system service has already validated the provider's eligibility. + * that the system service has already validated the provider's eligibility. A null value means + * no remote entry should be displayed for this provider. */ @Nullable public Entry getRemoteEntry() { @@ -77,6 +97,8 @@ public final class CreateCredentialProviderInfo { * * @hide */ + @SystemApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public static final class Builder { @NonNull private String mProviderName; @@ -85,7 +107,12 @@ public final class CreateCredentialProviderInfo { @Nullable private Entry mRemoteEntry = null; - /** Constructor with required properties. */ + /** + * Constructs a {@link CreateCredentialProviderInfo.Builder}. + * + * @param providerName the provider (component or package) name + * @throws IllegalArgumentException if {@code providerName} is null or empty + */ public Builder(@NonNull String providerName) { mProviderName = Preconditions.checkStringNotEmpty(providerName); } @@ -97,7 +124,13 @@ public final class CreateCredentialProviderInfo { return this; } - /** Sets the remote entry of the provider. */ + /** + * Sets the remote entry to be displayed to the user. + * + * The system service should only set this entry to non-null if it has validated that + * the given provider does have the permission to set this value. Null means there is + * no valid remote entry for display. + */ @NonNull public Builder setRemoteEntry(@Nullable Entry remoteEntry) { mRemoteEntry = remoteEntry; diff --git a/core/java/android/credentials/selection/DisabledProviderData.java b/core/java/android/credentials/selection/DisabledProviderData.java index b6f6ad4d60d7..4238612bedba 100644 --- a/core/java/android/credentials/selection/DisabledProviderData.java +++ b/core/java/android/credentials/selection/DisabledProviderData.java @@ -63,14 +63,14 @@ public final class DisabledProviderData extends ProviderData implements Parcelab } public static final @NonNull Creator<DisabledProviderData> CREATOR = new Creator<>() { - @Override - public DisabledProviderData createFromParcel(@NonNull Parcel in) { - return new DisabledProviderData(in); - } + @Override + public DisabledProviderData createFromParcel(@NonNull Parcel in) { + return new DisabledProviderData(in); + } - @Override - public DisabledProviderData[] newArray(int size) { - return new DisabledProviderData[size]; - } + @Override + public DisabledProviderData[] newArray(int size) { + return new DisabledProviderData[size]; + } }; } diff --git a/core/java/android/credentials/selection/DisabledProviderInfo.java b/core/java/android/credentials/selection/DisabledProviderInfo.java index 7d7dbc2dd689..7764d2e57995 100644 --- a/core/java/android/credentials/selection/DisabledProviderInfo.java +++ b/core/java/android/credentials/selection/DisabledProviderInfo.java @@ -16,17 +16,36 @@ package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.Context; +import android.credentials.CreateCredentialRequest; +import android.os.CancellationSignal; +import android.os.OutcomeReceiver; import com.android.internal.util.Preconditions; +import java.util.concurrent.Executor; + /** * Information pertaining to a specific provider that is disabled from the user settings. * - * Currently, disabled provider data is only propagated in the create-credential flow. + * Currently, disabled provider data is only propagated in the + * {@link android.credentials.CredentialManager#createCredential(Context, CreateCredentialRequest, + * CancellationSignal, Executor, OutcomeReceiver)} flow. + * + * This should be used to display an option, e.g. "+ Enable `disabled_provider_1`, + * `disabled_provider_2`" to navigate the user to Settings + * ({@link android.provider.Settings#ACTION_CREDENTIAL_PROVIDER}) to enable these + * disabled providers. * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class DisabledProviderInfo { @NonNull @@ -37,8 +56,7 @@ public final class DisabledProviderInfo { * * @throws IllegalArgumentException if {@code providerName} is empty */ - public DisabledProviderInfo( - @NonNull String providerName) { + public DisabledProviderInfo(@NonNull String providerName) { mProviderName = Preconditions.checkStringNotEmpty(providerName); } diff --git a/core/java/android/credentials/selection/Entry.java b/core/java/android/credentials/selection/Entry.java index bcf4ee3fb819..a131f3f5ecd5 100644 --- a/core/java/android/credentials/selection/Entry.java +++ b/core/java/android/credentials/selection/Entry.java @@ -22,7 +22,7 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.annotation.TestApi; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.app.slice.Slice; import android.content.Intent; @@ -36,7 +36,7 @@ import com.android.internal.util.AnnotationValidations; * * @hide */ -@TestApi +@SystemApi @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class Entry implements Parcelable { @NonNull @@ -67,30 +67,34 @@ public final class Entry implements Parcelable { } /** - * Constructor to be used for an entry that does not require further activities - * to be invoked when selected. - */ - // TODO(b/322065508): deprecate this constructor. - public Entry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice) { - mKey = key; - mSubkey = subkey; - mSlice = slice; - } - - /** * Constructor to be used for an entry that requires a pending intent to be invoked * when clicked. + * + * @param key the identifier of this entry that's unique within the context of the given + * CredentialManager request. This is used when constructing the + * {@link android.credentials.selection.UserSelectionResult#UserSelectionResult( + * String providerId, String entryKey, String entrySubkey, + * ProviderPendingIntentResponse providerPendingIntentResponse)} + * @param subkey the sub-identifier of this entry that's unique within the context of the + * {@code key}. This is used when constructing the + * {@link android.credentials.selection.UserSelectionResult#UserSelectionResult( + * String providerId, String entryKey, String entrySubkey, + * ProviderPendingIntentResponse providerPendingIntentResponse)} + * @param intent the intent containing extra data that has to be filled in when launching this + * entry's provider PendingIntent + * @param slice the Slice to be displayed */ public Entry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice, @NonNull Intent intent) { - this(key, subkey, slice); + mKey = key; + mSubkey = subkey; + mSlice = slice; mFrameworkExtrasIntent = intent; } /** * Returns the identifier of this entry that's unique within the context of the - * CredentialManager - * request. + * CredentialManager request. * * Generally used when sending the user selection result back to the system service. */ @@ -116,17 +120,10 @@ public final class Entry implements Parcelable { } /** - * Returns the provider PendingIntent to launch once this entry is selected. - */ - // TODO(b/322065508): deprecate this bit. - @Nullable - public PendingIntent getPendingIntent() { - return mPendingIntent; - } - - /** - * Returns the framework fill in intent to add to the provider PendingIntent to launch, once - * this entry is selected. + * Returns the intent containing extra data that has to be filled in when launching this + * entry's provider PendingIntent. + * + * If null, the provider PendingIntent can be launched without any fill in intent. */ @Nullable @SuppressLint("IntentBuilderName") // Not building a new intent. diff --git a/core/java/android/credentials/selection/GetCredentialProviderInfo.java b/core/java/android/credentials/selection/GetCredentialProviderInfo.java index db0fb84fa76c..1e2f27cddaec 100644 --- a/core/java/android/credentials/selection/GetCredentialProviderInfo.java +++ b/core/java/android/credentials/selection/GetCredentialProviderInfo.java @@ -16,21 +16,45 @@ package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; +import android.credentials.GetCredentialRequest; +import android.credentials.PrepareGetCredentialResponse; +import android.os.CancellationSignal; +import android.os.OutcomeReceiver; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** - * Information pertaining to a specific provider during the given create-credential flow. + * Information pertaining to a specific provider during the given + * {@link android.credentials.CredentialManager#getCredential(Context, GetCredentialRequest, + * CancellationSignal, Executor, OutcomeReceiver)} or + * {@link android.credentials.CredentialManager#getCredential(Context, + * PrepareGetCredentialResponse.PendingGetCredentialHandle, CancellationSignal, Executor, + * OutcomeReceiver)} flow. * * This includes provider metadata and its credential creation options for display purposes. * + * The selection UI should render all options (from + * {@link GetCredentialProviderInfo#getRemoteEntry()}, + * {@link GetCredentialProviderInfo#getCredentialEntries()}, and + * {@link GetCredentialProviderInfo#getActionChips()}) offered by this provider while clearly + * associated them with the given provider using the provider icon, label, etc. derived from + * {@link GetCredentialProviderInfo#getProviderName()}. + * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class GetCredentialProviderInfo { @NonNull @@ -95,8 +119,9 @@ public final class GetCredentialProviderInfo { /** * Returns the remote credential retrieval option, if any. * - * Notice that only one system configured provider can set this option, and when set, it means - * that the system service has already validated the provider's eligibility. + * Notice that only one system configured provider can set this option, and when set to + * non-null, it means that the system service has already validated the provider's eligibility. + * A null value means no remote entry should be displayed for this provider. */ @Nullable public Entry getRemoteEntry() { @@ -108,6 +133,8 @@ public final class GetCredentialProviderInfo { * * @hide */ + @SystemApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public static final class Builder { @NonNull private String mProviderName; @@ -123,6 +150,7 @@ public final class GetCredentialProviderInfo { /** * Constructs a {@link GetCredentialProviderInfo.Builder}. * + * @param providerName the provider (component or package) name * @throws IllegalArgumentException if {@code providerName} is null or empty */ public Builder(@NonNull String providerName) { @@ -151,7 +179,13 @@ public final class GetCredentialProviderInfo { return this; } - /** Sets the remote entry to be displayed to the user. */ + /** + * Sets the remote entry to be displayed to the user. + * + * The system service should only set this entry to non-null if it has validated that + * the given provider does have the permission to set this value. Null means there is + * no valid remote entry for display. + */ @NonNull public Builder setRemoteEntry(@Nullable Entry remoteEntry) { mRemoteEntry = remoteEntry; diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java index c3a09ae61754..d0f8a4044dc1 100644 --- a/core/java/android/credentials/selection/IntentFactory.java +++ b/core/java/android/credentials/selection/IntentFactory.java @@ -97,8 +97,6 @@ public class IntentFactory { /** * Creates an Intent that cancels any UI matching the given request token id. - * - * @hide */ @NonNull public static Intent createCancelUiIntent(@NonNull IBinder requestToken, @@ -111,8 +109,8 @@ public class IntentFactory { com.android.internal.R.string .config_credentialManagerDialogComponent)); intent.setComponent(componentName); - intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, - new CancelUiRequest(requestToken, shouldShowCancellationUi, appPackageName)); + intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, + new CancelSelectionRequest(requestToken, shouldShowCancellationUi, appPackageName)); return intent; } diff --git a/core/java/android/credentials/selection/IntentHelper.java b/core/java/android/credentials/selection/IntentHelper.java index 6bcd05afc00f..2c3a320b6750 100644 --- a/core/java/android/credentials/selection/IntentHelper.java +++ b/core/java/android/credentials/selection/IntentHelper.java @@ -16,12 +16,16 @@ package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.content.Intent; import android.os.ResultReceiver; +import java.util.Collections; import java.util.List; /** @@ -29,15 +33,17 @@ import java.util.List; * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class IntentHelper { /** - * Attempts to extract a {@link CancelUiRequest} from the given intent; returns null + * Attempts to extract a {@link CancelSelectionRequest} from the given intent; returns null * if not found. */ @Nullable - public static CancelUiRequest extractCancelUiRequest(@NonNull Intent intent) { - return intent.getParcelableExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, - CancelUiRequest.class); + public static CancelSelectionRequest extractCancelUiRequest(@NonNull Intent intent) { + return intent.getParcelableExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, + CancelSelectionRequest.class); } /** @@ -52,37 +58,44 @@ public final class IntentHelper { /** * Attempts to extract the list of {@link GetCredentialProviderInfo} from the given intent; - * returns null if not found. + * returns an empty list if not found. */ - @Nullable - @SuppressLint("NullableCollection") // To be consistent with the nullable Intent extra APIs - // and the other APIs in this class. - public static List<GetCredentialProviderInfo> extractGetCredentialProviderDataList( + public static @NonNull List<GetCredentialProviderInfo> extractGetCredentialProviderInfoList( @NonNull Intent intent) { List<GetCredentialProviderData> providerList = intent.getParcelableArrayListExtra( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, GetCredentialProviderData.class); - return providerList == null ? null : providerList.stream().map( + return providerList == null ? Collections.emptyList() : providerList.stream().map( GetCredentialProviderData::toGetCredentialProviderInfo).toList(); } /** * Attempts to extract the list of {@link CreateCredentialProviderInfo} from the given intent; - * returns null if not found. + * returns an empty list if not found. */ - @Nullable - @SuppressLint("NullableCollection") // To be consistent with the nullable Intent extra APIs - // and the other APIs in this class. - public static List<CreateCredentialProviderInfo> extractCreateCredentialProviderDataList( - @NonNull Intent intent) { + public static @NonNull List<CreateCredentialProviderInfo> + extractCreateCredentialProviderInfoList(@NonNull Intent intent) { List<CreateCredentialProviderData> providerList = intent.getParcelableArrayListExtra( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, CreateCredentialProviderData.class); - return providerList == null ? null : providerList.stream().map( + return providerList == null ? Collections.emptyList() : providerList.stream().map( CreateCredentialProviderData::toCreateCredentialProviderInfo).toList(); } /** + * Attempts to extract the list of {@link DisabledProviderInfo} from the given intent; + * returns an empty list if not found. + */ + public static @NonNull List<DisabledProviderInfo> extractDisabledProviderInfoList( + @NonNull Intent intent) { + List<DisabledProviderData> providerList = intent.getParcelableArrayListExtra( + ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, + DisabledProviderData.class); + return providerList == null ? Collections.emptyList() : providerList.stream().map( + DisabledProviderData::toDisabledProviderInfo).toList(); + } + + /** * Attempts to extract a {@link android.os.ResultReceiver} from the given intent, which should * be used to send back UI results; returns null if not found. */ diff --git a/core/java/android/credentials/selection/RequestInfo.java b/core/java/android/credentials/selection/RequestInfo.java index 7d6ea7ee6b8c..2fd322adb79c 100644 --- a/core/java/android/credentials/selection/RequestInfo.java +++ b/core/java/android/credentials/selection/RequestInfo.java @@ -22,6 +22,7 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.credentials.CreateCredentialRequest; import android.credentials.GetCredentialRequest; @@ -41,13 +42,15 @@ import java.util.List; * * @hide */ -@TestApi +@SystemApi @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class RequestInfo implements Parcelable { /** * The intent extra key for the {@code RequestInfo} object when launching the UX * activities. + * + * @hide */ @NonNull public static final String EXTRA_REQUEST_INFO = @@ -79,7 +82,7 @@ public final class RequestInfo implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef(value = {TYPE_GET, TYPE_CREATE}) + @StringDef(value = {TYPE_GET, TYPE_CREATE, TYPE_UNDEFINED}) public @interface RequestType { } @@ -107,18 +110,13 @@ public final class RequestInfo implements Parcelable { private final boolean mHasPermissionToOverrideDefault; - /** Creates new {@code RequestInfo} for a create-credential flow. */ - @NonNull - public static RequestInfo newCreateRequestInfo( - @NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest, - @NonNull String appPackageName) { - return new RequestInfo( - token, TYPE_CREATE, appPackageName, createCredentialRequest, null, - /*hasPermissionToOverrideDefault=*/ false, - /*defaultProviderIds=*/ new ArrayList<>()); - } - - /** Creates new {@code RequestInfo} for a create-credential flow. */ + /** + * Creates new {@code RequestInfo} for a create-credential flow. + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) @NonNull public static RequestInfo newCreateRequestInfo( @NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest, @@ -129,7 +127,13 @@ public final class RequestInfo implements Parcelable { hasPermissionToOverrideDefault, defaultProviderIds); } - /** Creates new {@code RequestInfo} for a get-credential flow. */ + /** + * Creates new {@code RequestInfo} for a get-credential flow. + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) @NonNull public static RequestInfo newGetRequestInfo( @NonNull IBinder token, @NonNull GetCredentialRequest getCredentialRequest, @@ -140,24 +144,17 @@ public final class RequestInfo implements Parcelable { /*defaultProviderIds=*/ new ArrayList<>()); } - /** Creates new {@code RequestInfo} for a get-credential flow. */ - @NonNull - public static RequestInfo newGetRequestInfo( - @NonNull IBinder token, @NonNull GetCredentialRequest getCredentialRequest, - @NonNull String appPackageName) { - return new RequestInfo( - token, TYPE_GET, appPackageName, null, getCredentialRequest, - /*hasPermissionToOverrideDefault=*/ false, - /*defaultProviderIds=*/ new ArrayList<>()); - } - /** Returns whether the calling package has the permission. */ public boolean hasPermissionToOverrideDefault() { return mHasPermissionToOverrideDefault; } - /** Returns the request token matching the user request. */ + /** + * Returns the request token matching the user request. + * + * @hide + */ @NonNull public IBinder getToken() { return mToken; @@ -185,6 +182,12 @@ public final class RequestInfo implements Parcelable { return mCreateCredentialRequest; } + /** Returns the request token matching the app request that should be cancelled. */ + @NonNull + public RequestToken getRequestToken() { + return new RequestToken(mToken); + } + /** * Returns default provider identifiers (component or package name) configured from the user * settings. diff --git a/core/java/android/credentials/selection/RequestToken.java b/core/java/android/credentials/selection/RequestToken.java new file mode 100644 index 000000000000..27b83f873732 --- /dev/null +++ b/core/java/android/credentials/selection/RequestToken.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.credentials.selection; + +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.IBinder; + +/** + * Unique identifier for a getCredential / createCredential API session. + * + * To compare if two requests pertain to the same session, compare their RequestTokens using + * the {@link RequestToken#equals(Object)} method. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) +public final class RequestToken { + + @NonNull + private final IBinder mToken; + + /** @hide */ + @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) + public RequestToken(@NonNull IBinder token) { + mToken = token; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || !(obj instanceof RequestToken)) { + return false; + } + final RequestToken other = (RequestToken) obj; + return mToken.equals(other.mToken); + } + + @Override + public int hashCode() { + return mToken.hashCode(); + } +} diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt index 0fa248de4465..9a2cf61d2b86 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt @@ -36,7 +36,7 @@ fun Intent.parse( fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? = this.cancelUiRequest?.let { cancelUiRequest -> - val showCancel = cancelUiRequest.shouldShowCancellationUi().apply { + val showCancel = cancelUiRequest.shouldShowCancellationExplanation().apply { Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this") } if (showCancel) { diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt index 4155b0398ce0..9242141cfd63 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt @@ -17,7 +17,7 @@ package com.android.credentialmanager.ktx import android.content.Intent -import android.credentials.selection.CancelUiRequest +import android.credentials.selection.CancelSelectionRequest import android.credentials.selection.Constants import android.credentials.selection.CreateCredentialProviderData import android.credentials.selection.GetCredentialProviderData @@ -25,10 +25,10 @@ import android.credentials.selection.ProviderData import android.credentials.selection.RequestInfo import android.os.ResultReceiver -val Intent.cancelUiRequest: CancelUiRequest? +val Intent.cancelUiRequest: CancelSelectionRequest? get() = this.extras?.getParcelable( - CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, - CancelUiRequest::class.java + CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, + CancelSelectionRequest::class.java ) val Intent.requestInfo: RequestInfo? diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 6cafcf7cd976..f77e7cfdffef 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -18,7 +18,7 @@ package com.android.credentialmanager import android.content.Context import android.content.Intent -import android.credentials.selection.CancelUiRequest +import android.credentials.selection.CancelSelectionRequest import android.credentials.selection.Constants import android.credentials.selection.CreateCredentialProviderData import android.credentials.selection.GetCredentialProviderData @@ -275,10 +275,10 @@ class CredentialManagerRepo( } /** Return the cancellation request if present. */ - fun getCancelUiRequest(intent: Intent): CancelUiRequest? { + fun getCancelUiRequest(intent: Intent): CancelSelectionRequest? { return intent.extras?.getParcelable( - CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, - CancelUiRequest::class.java + CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST, + CancelSelectionRequest::class.java ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index fa975aabc867..05aa5489ff36 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -131,7 +131,7 @@ class CredentialSelectorActivity : ComponentActivity() { // Cancellation was for a different request, don't cancel the current UI. return Triple(true, false, null) } - val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationUi() + val shouldShowCancellationUi = cancelUiRequest.shouldShowCancellationExplanation() Log.d( Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" + " ui = $shouldShowCancellationUi") |