Similar to the create flow, make get request two staged.
Similar to the create flow, we will also split the get flow into the
candidate query stage (for displaying options) and the execution stage
(for getting a credential after a user makes the choice). This requires
the api to expose two request data getters: getCandidateQueryData for
the query stage and getRequestData for the execution stage.
Bug: 246564035
Bug: 253153445
CTS-Coverage-Bug: 246637346
Test: Local Build & Deployment
Change-Id: Ic63d8b257376beec039d8867e5b3ea1ab2d284e7
diff --git a/core/api/current.txt b/core/api/current.txt
index 434b60d..fb3fd0c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13096,9 +13096,10 @@
}
public final class GetCredentialOption implements android.os.Parcelable {
- ctor public GetCredentialOption(@NonNull String, @NonNull android.os.Bundle, boolean);
+ ctor public GetCredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
method public int describeContents();
- method @NonNull public android.os.Bundle getData();
+ method @NonNull public android.os.Bundle getCandidateQueryData();
+ method @NonNull public android.os.Bundle getCredentialRetrievalData();
method @NonNull public String getType();
method public boolean requireSystemProvider();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java
index 4589039..be887a5 100644
--- a/core/java/android/credentials/CreateCredentialRequest.java
+++ b/core/java/android/credentials/CreateCredentialRequest.java
@@ -52,7 +52,7 @@
private final Bundle mCandidateQueryData;
/**
- * Determines whether or not the request must only be fulfilled by a system provider.
+ * Determines whether the request must only be fulfilled by a system provider.
*/
private final boolean mRequireSystemProvider;
diff --git a/core/java/android/credentials/GetCredentialOption.java b/core/java/android/credentials/GetCredentialOption.java
index ed93dae..47731dd 100644
--- a/core/java/android/credentials/GetCredentialOption.java
+++ b/core/java/android/credentials/GetCredentialOption.java
@@ -38,13 +38,20 @@
private final String mType;
/**
- * The request data.
+ * The full request data.
*/
@NonNull
- private final Bundle mData;
+ private final Bundle mCredentialRetrievalData;
/**
- * Determines whether or not the request must only be fulfilled by a system provider.
+ * The partial request data that will be sent to the provider during the initial credential
+ * candidate query stage.
+ */
+ @NonNull
+ private final Bundle mCandidateQueryData;
+
+ /**
+ * Determines whether the request must only be fulfilled by a system provider.
*/
private final boolean mRequireSystemProvider;
@@ -57,11 +64,27 @@
}
/**
- * Returns the request data.
+ * Returns the full request data.
*/
@NonNull
- public Bundle getData() {
- return mData;
+ public Bundle getCredentialRetrievalData() {
+ return mCredentialRetrievalData;
+ }
+
+ /**
+ * Returns the partial request data that will be sent to the provider during the initial
+ * credential candidate query stage.
+ *
+ * For security reason, a provider will receive the request data in two stages. First it gets
+ * this partial request that do not contain sensitive user information; it uses this
+ * information to provide credential candidates that the [@code CredentialManager] will show to
+ * the user. Next, the full request data, {@link #getCredentialRetrievalData()}, will be sent to
+ * a provider only if the user further grants the consent by choosing a candidate from the
+ * provider.
+ */
+ @NonNull
+ public Bundle getCandidateQueryData() {
+ return mCandidateQueryData;
}
/**
@@ -75,7 +98,8 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType);
- dest.writeBundle(mData);
+ dest.writeBundle(mCredentialRetrievalData);
+ dest.writeBundle(mCandidateQueryData);
dest.writeBoolean(mRequireSystemProvider);
}
@@ -88,7 +112,8 @@
public String toString() {
return "GetCredentialOption {"
+ "type=" + mType
- + ", data=" + mData
+ + ", requestData=" + mCredentialRetrievalData
+ + ", candidateQueryData=" + mCandidateQueryData
+ ", requireSystemProvider=" + mRequireSystemProvider
+ "}";
}
@@ -96,44 +121,52 @@
/**
* Constructs a {@link GetCredentialOption}.
*
- * @param type the requested credential type
- * @param data the request data
- * @param requireSystemProvider whether or not the request must only be fulfilled by a system
- * provider
- *
+ * @param type the requested credential type
+ * @param credentialRetrievalData the request data
+ * @param candidateQueryData the partial request data that will be sent to the provider
+ * during the initial credential candidate query stage
+ * @param requireSystemProvider whether the request must only be fulfilled by a system
+ * provider
* @throws IllegalArgumentException If type is empty.
*/
public GetCredentialOption(
@NonNull String type,
- @NonNull Bundle data,
+ @NonNull Bundle credentialRetrievalData,
+ @NonNull Bundle candidateQueryData,
boolean requireSystemProvider) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
- mData = requireNonNull(data, "data must not be null");
+ mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
+ "requestData must not be null");
+ mCandidateQueryData = requireNonNull(candidateQueryData,
+ "candidateQueryData must not be null");
mRequireSystemProvider = requireSystemProvider;
}
private GetCredentialOption(@NonNull Parcel in) {
String type = in.readString8();
Bundle data = in.readBundle();
+ Bundle candidateQueryData = in.readBundle();
boolean requireSystemProvider = in.readBoolean();
mType = type;
AnnotationValidations.validate(NonNull.class, null, mType);
- mData = data;
- AnnotationValidations.validate(NonNull.class, null, mData);
+ mCredentialRetrievalData = data;
+ AnnotationValidations.validate(NonNull.class, null, mCredentialRetrievalData);
+ mCandidateQueryData = candidateQueryData;
+ AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
mRequireSystemProvider = requireSystemProvider;
}
public static final @NonNull Parcelable.Creator<GetCredentialOption> CREATOR =
new Parcelable.Creator<GetCredentialOption>() {
- @Override
- public GetCredentialOption[] newArray(int size) {
- return new GetCredentialOption[size];
- }
+ @Override
+ public GetCredentialOption[] newArray(int size) {
+ return new GetCredentialOption[size];
+ }
- @Override
- public GetCredentialOption createFromParcel(@NonNull Parcel in) {
- return new GetCredentialOption(in);
- }
- };
+ @Override
+ public GetCredentialOption createFromParcel(@NonNull Parcel in) {
+ return new GetCredentialOption(in);
+ }
+ };
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 2780c3c..f801ba6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -518,7 +518,7 @@
GetCredentialRequest.Builder()
.addGetCredentialOption(
GetCredentialOption(
- TYPE_PUBLIC_KEY_CREDENTIAL, Bundle(), /*requireSystemProvider=*/ false)
+ TYPE_PUBLIC_KEY_CREDENTIAL, Bundle(), Bundle(), /*requireSystemProvider=*/ false)
)
.build(),
/*isFirstUsage=*/false,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
index eb65241..ef48a77 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/GetCredentialOption.kt
@@ -28,9 +28,9 @@
* otherwise
*/
open class GetCredentialOption(
- val type: String,
- val data: Bundle,
- val requireSystemProvider: Boolean,
+ val type: String,
+ val data: Bundle,
+ val requireSystemProvider: Boolean,
) {
companion object {
@JvmStatic
@@ -38,14 +38,20 @@
return try {
when (from.type) {
Credential.TYPE_PASSWORD_CREDENTIAL ->
- GetPasswordOption.createFrom(from.data)
+ GetPasswordOption.createFrom(from.credentialRetrievalData)
PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
- GetPublicKeyCredentialBaseOption.createFrom(from.data)
+ GetPublicKeyCredentialBaseOption.createFrom(from.credentialRetrievalData)
else ->
- GetCredentialOption(from.type, from.data, from.requireSystemProvider())
+ GetCredentialOption(
+ from.type, from.credentialRetrievalData, from.requireSystemProvider()
+ )
}
} catch (e: FrameworkClassParsingException) {
- GetCredentialOption(from.type, from.data, from.requireSystemProvider())
+ GetCredentialOption(
+ from.type,
+ from.credentialRetrievalData,
+ from.requireSystemProvider()
+ )
}
}
}