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