summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFeng Cao <fengcao@google.com>2020-06-25 11:04:40 -0700
committerFeng Cao <fengcao@google.com>2020-06-25 17:39:13 -0700
commit32cfac9ed78c56cfb39fa6e66d0a4cbf3363f0a9 (patch)
tree8c1ddabf78e712c39c9d4c0da04507aa166a5abd
parentb0f26f32d8d59c52e2fcbf4954451a6edbeac29e (diff)
Do not replace the authenticated dataset for pinned inline suggestion
* For the case where the pinned inline suggestion triggers an pending intent through the auth flow, and it returns a dataset to be autofilled, previously we would replace the existing dataset with the returned dataset. However, this is causing several potential issues: 1. if the returned dataset doesn't contain inline presentation the the pinned icon will not show again 2. if the returned dataset contains inline presentation but not the pending intent, then the pinned icon will show up again but tapping on it will not launch the pending intent 3. if the returned dataset contains the inline presentaion and the pending intent, then when we "autofill" it, it'll fire the pending intent directly as opposed to filling in the value * We fix the issue by not replacing the old dataset if the dataset is a pinned inline suggestion. * One caveat of the approach is that: a dataset can potentially have multiple fields, and it's possible that some of the fields' has inline presentation and some don't. It's also possible that some of the fields' inline presentation is pinned and some isn't. So the concept of whether a Dataset is pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the cases. * An alternative approach is to have the autofill provider telling whether they want to replace the old dataset or not, through a new field in the returned Bundle. But that requres an API change so is infeasible at this time. Test: atest android.autofillservice.cts.inline Test: atest android.autofillservice.cts.augmented Test: atest android.autofillservice.cts.LoginActivityTest Test: atest android.autofillservice.cts.AuthenticationTest Bug: 159367101 Change-Id: I6d162aeb88a4655989c1aa315df8304c0980ac60
-rw-r--r--core/java/android/service/autofill/Dataset.java7
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java20
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java27
3 files changed, 48 insertions, 6 deletions
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 08aa534be152..2d99c413cc89 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -312,7 +312,12 @@ public final class Dataset implements Parcelable {
* setting it to the {@link
* android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you
* provide a dataset in the result, it will replace the authenticated dataset and
- * will be immediately filled in. If you provide a response, it will replace the
+ * will be immediately filled in. An exception to this behavior is if the original
+ * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset
+ * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then
+ * the original dataset will not be replaced,
+ * so that it can be triggered as a pending intent again.
+ * If you provide a response, it will replace the
* current response and the UI will be refreshed. For example, if you provided
* credit card information without the CVV for the data set in the {@link FillResponse
* response} then the returned data set should contain the CVV entry.
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 9cf1b87f7eab..914169485979 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -50,7 +50,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
private final boolean mPinned;
@@ -90,7 +94,11 @@ public final class InlinePresentation implements Parcelable {
* Specifies the UI specification for the inline suggestion.
* @param pinned
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public InlinePresentation(
@@ -126,7 +134,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public boolean isPinned() {
@@ -232,7 +244,7 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1586992400667L,
+ time = 1593131904745L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9b3d075e3f2c..7ab4369b338a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -73,6 +73,7 @@ import android.service.autofill.FieldClassificationUserData;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
@@ -1437,7 +1438,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClientState = newClientState;
}
final Dataset dataset = (Dataset) result;
- authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+ final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
+ if (!isPinnedDataset(oldDataset)) {
+ authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+ }
autoFill(requestId, datasetIdx, dataset, false);
} else {
Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
@@ -1455,6 +1459,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ /**
+ * A dataset can potentially have multiple fields, and it's possible that some of the fields'
+ * has inline presentation and some don't. It's also possible that some of the fields'
+ * inline presentation is pinned and some isn't. So the concept of whether a dataset is
+ * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a
+ * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient
+ * for most of the cases.
+ */
+ private static boolean isPinnedDataset(@Nullable Dataset dataset) {
+ if (dataset != null && dataset.getFieldIds() != null) {
+ final int numOfFields = dataset.getFieldIds().size();
+ for (int i = 0; i < numOfFields; i++) {
+ final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(i);
+ if (inlinePresentation != null && inlinePresentation.isPinned()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mLock")
void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
final Dataset dataset = (data == null) ? null :