Merge "Add a new Autofill FillEvent - for when the user taps a field. Test: atest android.autofillservice.cts.inline.InlineFillEventHistoryTest" into udc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index dc2cec0..272fab3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -39862,6 +39862,7 @@
field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+ field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index b0e847c..5d58120 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -233,6 +233,22 @@
*/
public static final int TYPE_DATASETS_SHOWN = 5;
+ /**
+ * The app/user requested for a field to be Autofilled.
+ *
+ * This event is fired when the view has been entered (by user or app) in order
+ * to differentiate from FillRequests that have been pretriggered for FillDialogs.
+ *
+ * For example, the user might navigate away from a screen without tapping any
+ * fields. In this case, a FillRequest/FillResponse has been generated, but was
+ * not used for Autofilling. The user did not intend to see an Autofill result,
+ * but a FillRequest was still generated. This is different from when the user
+ * did tap on a field after the pretriggered FillRequest, this event will appear
+ * in the FillEventHistory, signaling that the user did intend to Autofill
+ * something.
+ */
+ public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_DATASET_SELECTED,
@@ -240,7 +256,8 @@
TYPE_AUTHENTICATION_SELECTED,
TYPE_SAVE_SHOWN,
TYPE_CONTEXT_COMMITTED,
- TYPE_DATASETS_SHOWN
+ TYPE_DATASETS_SHOWN,
+ TYPE_VIEW_REQUESTED_AUTOFILL
})
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
@@ -659,8 +676,8 @@
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications,
int saveDialogNotShowReason, int uiType) {
- mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,
- "eventType");
+ mEventType = Preconditions.checkArgumentInRange(eventType, 0,
+ TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
mDatasetId = datasetId;
mClientState = clientState;
mSelectedDatasetIds = selectedDatasetIds;
@@ -723,6 +740,8 @@
return "TYPE_CONTEXT_COMMITTED";
case TYPE_DATASETS_SHOWN:
return "TYPE_DATASETS_SHOWN";
+ case TYPE_VIEW_REQUESTED_AUTOFILL:
+ return "TYPE_VIEW_REQUESTED_AUTOFILL";
default:
return "TYPE_UNKNOWN";
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 43b816b..61032dc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -849,6 +849,32 @@
}
}
+
+ /**
+ * Updates the last fill response when a view was entered.
+ */
+ void logViewEntered(int sessionId, @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (!isValidEventLocked("logViewEntered", sessionId)) {
+ return;
+ }
+
+ if (mEventHistory.getEvents() != null) {
+ // Do not log this event more than once
+ for (Event event : mEventHistory.getEvents()) {
+ if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
+ Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
+ return;
+ }
+ }
+ }
+
+ mEventHistory.addEvent(
+ new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
+ null, null, null, null, null, null, null));
+ }
+ }
+
void logAugmentedAutofillAuthenticationSelected(int sessionId, @Nullable String selectedDataset,
@Nullable Bundle clientState) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 598521f..4a12e38 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -442,6 +442,18 @@
private boolean mPreviouslyFillDialogPotentiallyStarted;
/**
+ * Keeps track of if the user entered view, this is used to
+ * distinguish Fill Request that did not have user interaction
+ * with ones that did.
+ *
+ * This is set to true when entering view - after FillDialog FillRequest
+ * or on plain user tap.
+ */
+ @NonNull
+ @GuardedBy("mLock")
+ private boolean mLogViewEntered;
+
+ /**
* Keeps the fill dialog trigger ids of the last response. This invalidates
* the trigger ids of the previous response.
*/
@@ -1289,6 +1301,7 @@
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
+ mLogViewEntered = false;
}
/**
@@ -1413,6 +1426,14 @@
mService.setLastResponse(id, response);
+ synchronized (mLock) {
+ if (mLogViewEntered) {
+ mLogViewEntered = false;
+ mService.logViewEntered(id, null);
+ }
+ }
+
+
final long disableDuration = response.getDisableDuration();
final boolean autofillDisabled = disableDuration > 0;
if (autofillDisabled) {
@@ -3545,6 +3566,28 @@
return;
}
+ synchronized (mLock) {
+ if (!mLogViewEntered) {
+ // If the current request is for FillDialog (preemptive)
+ // then this is the first time that the view is entered
+ // (mLogViewEntered == false) in this case, setLastResponse()
+ // has already been called, so just log here.
+ // If the current request is not and (mLogViewEntered == false)
+ // then the last session is being tracked (setLastResponse not called)
+ // so this calling logViewEntered will be a nop.
+ // Calling logViewEntered() twice will only log it once
+ // TODO(271181979): this is broken for multiple partitions
+ mService.logViewEntered(this.id, null);
+ }
+
+ // If this is the first time view is entered for inline, the last
+ // session is still being tracked, so logViewEntered() needs
+ // to be delayed until setLastResponse is called.
+ // For fill dialog requests case logViewEntered is already called above
+ // so this will do nothing. Assumption: only one fill dialog per session
+ mLogViewEntered = true;
+ }
+
// Previously, fill request will only start whenever a view is entered.
// With Fill Dialog, request starts prior to view getting entered. So, we can't end
// the event at this moment, otherwise we will be wrongly attributing fill dialog