summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt99
-rw-r--r--core/api/test-current.txt59
-rw-r--r--core/java/android/credentials/selection/AuthenticationEntry.java74
-rw-r--r--core/java/android/credentials/selection/CancelSelectionRequest.java147
-rw-r--r--core/java/android/credentials/selection/CancelUiRequest.java114
-rw-r--r--core/java/android/credentials/selection/CreateCredentialProviderData.java9
-rw-r--r--core/java/android/credentials/selection/CreateCredentialProviderInfo.java41
-rw-r--r--core/java/android/credentials/selection/DisabledProviderData.java16
-rw-r--r--core/java/android/credentials/selection/DisabledProviderInfo.java24
-rw-r--r--core/java/android/credentials/selection/Entry.java51
-rw-r--r--core/java/android/credentials/selection/GetCredentialProviderInfo.java42
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java6
-rw-r--r--core/java/android/credentials/selection/IntentHelper.java49
-rw-r--r--core/java/android/credentials/selection/RequestInfo.java57
-rw-r--r--core/java/android/credentials/selection/RequestToken.java65
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt2
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt8
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt8
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt2
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")