Merge "Log why presentation was not shown" into tm-dev
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 7353bbc..7d7270d 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -522,6 +522,61 @@
     public static final int RESULT_CODE_NOT_SERVICE = -1;
 
     /**
+     *  Reasons to commit the Autofill context.
+     *
+     *  <p>If adding a new reason, modify
+     *  {@link com.android.server.autofill.PresentationStatsEventLogger#getNoPresentationEventReason(int)}
+     *  as well.</p>
+     *
+     *  TODO(b/233833662): Expose this as a public API in U.
+     *  @hide
+     */
+    @IntDef(prefix = { "COMMIT_REASON_" }, value = {
+            COMMIT_REASON_UNKNOWN,
+            COMMIT_REASON_ACTIVITY_FINISHED,
+            COMMIT_REASON_VIEW_COMMITTED,
+            COMMIT_REASON_VIEW_CLICKED,
+            COMMIT_REASON_VIEW_CHANGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AutofillCommitReason {}
+
+    /**
+     * Autofill context was committed because of an unknown reason.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_UNKNOWN = 0;
+
+    /**
+     * Autofill context was committed because activity finished.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_ACTIVITY_FINISHED = 1;
+
+    /**
+     * Autofill context was committed because {@link #commit()} was called.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_VIEW_COMMITTED = 2;
+
+    /**
+     * Autofill context was committed because view was clicked.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_VIEW_CLICKED = 3;
+
+    /**
+     * Autofill context was committed because of view changed.
+     *
+     * @hide
+     */
+    public static final int COMMIT_REASON_VIEW_CHANGED = 4;
+
+    /**
      * Makes an authentication id from a request id and a dataset id.
      *
      * @param requestId The request id.
@@ -1585,7 +1640,7 @@
             }
             if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
                 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
-                commitLocked();
+                commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_CLICKED);
                 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
             }
         }
@@ -1603,7 +1658,7 @@
         synchronized (mLock) {
             if (mSaveOnFinish) {
                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
-                commitLocked();
+                commitLocked(/* commitReason= */ COMMIT_REASON_ACTIVITY_FINISHED);
             } else {
                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
                 cancelLocked();
@@ -1628,16 +1683,16 @@
         }
         if (sVerbose) Log.v(TAG, "commit() called by app");
         synchronized (mLock) {
-            commitLocked();
+            commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_COMMITTED);
         }
     }
 
     @GuardedBy("mLock")
-    private void commitLocked() {
+    private void commitLocked(@AutofillCommitReason int commitReason) {
         if (!mEnabled && !isActiveLocked()) {
             return;
         }
-        finishSessionLocked();
+        finishSessionLocked(/* commitReason= */ commitReason);
     }
 
     /**
@@ -2070,13 +2125,13 @@
     }
 
     @GuardedBy("mLock")
-    private void finishSessionLocked() {
+    private void finishSessionLocked(@AutofillCommitReason int commitReason) {
         if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
 
         if (!isActiveLocked()) return;
 
         try {
-            mService.finishSession(mSessionId, mContext.getUserId());
+            mService.finishSession(mSessionId, mContext.getUserId(), commitReason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3549,7 +3604,7 @@
             }
 
             if (mVisibleTrackedIds == null) {
-                finishSessionLocked();
+                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
             }
         }
 
@@ -3582,9 +3637,9 @@
 
             if (mVisibleTrackedIds == null) {
                 if (sVerbose) {
-                    Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
+                    Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
                 }
-                finishSessionLocked();
+                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
             }
         }
 
@@ -3656,7 +3711,7 @@
                 if (sVerbose) {
                     Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
                 }
-                finishSessionLocked();
+                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
             }
         }
     }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index a507e74..cefd6dc 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -49,7 +49,7 @@
     void updateSession(int sessionId, in AutofillId id, in Rect bounds,
         in AutofillValue value, int action, int flags, int userId);
     void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
-    void finishSession(int sessionId, int userId);
+    void finishSession(int sessionId, int userId, int commitReason);
     void cancelSession(int sessionId, int userId);
     void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
     void setHasCallback(int sessionId, int userId, boolean hasIt);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index fc95cdd..15d3fa9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -69,6 +69,7 @@
 import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.AutofillCommitReason;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
 import android.view.autofill.AutofillManagerInternal;
 import android.view.autofill.AutofillValue;
@@ -1657,11 +1658,12 @@
         }
 
         @Override
-        public void finishSession(int sessionId, int userId) {
+        public void finishSession(int sessionId, int userId,
+                @AutofillCommitReason int commitReason) {
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    service.finishSessionLocked(sessionId, getCallingUid());
+                    service.finishSessionLocked(sessionId, getCallingUid(), commitReason);
                 } else if (sVerbose) {
                     Slog.v(TAG, "finishSession(): no service for " + userId);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index aa5c501..fe85db2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -70,6 +70,7 @@
 import android.util.SparseArray;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.AutofillCommitReason;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
@@ -423,7 +424,7 @@
     }
 
     @GuardedBy("mLock")
-    void finishSessionLocked(int sessionId, int uid) {
+    void finishSessionLocked(int sessionId, int uid, @AutofillCommitReason int commitReason) {
         if (!isEnabledLocked()) {
             return;
         }
@@ -438,7 +439,7 @@
 
         final Session.SaveResult saveResult = session.showSaveLocked();
 
-        session.logContextCommitted(saveResult.getNoSaveUiReason());
+        session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason);
 
         if (saveResult.isLogSaveShown()) {
             session.logSaveUiShown();
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
new file mode 100644
index 0000000..c6e595b
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 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 com.android.server.autofill;
+
+import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
+import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
+import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
+import static android.service.autofill.FillEventHistory.Event.UiType;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_ACTIVITY_FINISHED;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CHANGED;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CLICKED;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_COMMITTED;
+
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.service.autofill.Dataset;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Optional;
+
+/** Helper class to track and log Autofill presentation stats. */
+public final class PresentationStatsEventLogger {
+    private static final String TAG = "PresentationStatsEventLogger";
+
+    /**
+     * Reasons why presentation was not shown. These are wrappers around
+     * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.PresentationEventResult}.
+     */
+    @IntDef(prefix = { "NOT_SHOWN_REASON" }, value = {
+            NOT_SHOWN_REASON_ANY_SHOWN,
+            NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
+            NOT_SHOWN_REASON_VIEW_CHANGED,
+            NOT_SHOWN_REASON_ACTIVITY_FINISHED,
+            NOT_SHOWN_REASON_REQUEST_TIMEOUT,
+            NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
+            NOT_SHOWN_REASON_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NotShownReason {}
+    public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
+    public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+    public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
+    public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
+    public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
+    public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
+    public static final int NOT_SHOWN_REASON_UNKNOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
+
+    private final int mSessionId;
+    private Optional<PresentationStatsEventInternal> mEventInternal;
+
+    private PresentationStatsEventLogger(int sessionId) {
+        mSessionId = sessionId;
+        mEventInternal = Optional.empty();
+    }
+
+    public static PresentationStatsEventLogger forSessionId(int sessionId) {
+        return new PresentationStatsEventLogger(sessionId);
+    }
+
+    public void startNewEvent() {
+        if (mEventInternal.isPresent()) {
+            Slog.e(TAG, "Failed to start new event because already have active event.");
+            return;
+        }
+        mEventInternal = Optional.of(new PresentationStatsEventInternal());
+    }
+
+    public void maybeSetRequestId(int requestId) {
+        mEventInternal.ifPresent(event -> event.mRequestId = requestId);
+    }
+
+    public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
+        mEventInternal.ifPresent(event -> {
+            if (event.mCountShown == 0) {
+                event.mNoPresentationReason = reason;
+            }
+        });
+    }
+
+    public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
+            AutofillId currentViewId) {
+        mEventInternal.ifPresent(event -> {
+            int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
+            event.mAvailableCount = availableCount;
+            event.mIsDatasetAvailable = availableCount > 0;
+        });
+    }
+
+    public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
+            AutofillId currentViewId) {
+        mEventInternal.ifPresent(event -> {
+            int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
+            event.mCountShown = countShown;
+            if (countShown > 0) {
+                event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
+            }
+        });
+    }
+
+    private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
+            AutofillId currentViewId) {
+        int availableCount = 0;
+        if (datasetList != null) {
+            for (int i = 0; i < datasetList.size(); i++) {
+                Dataset data = datasetList.get(i);
+                if (data != null && data.getFieldIds() != null
+                        && data.getFieldIds().contains(currentViewId)) {
+                    availableCount += 1;
+                }
+            }
+        }
+        return availableCount;
+    }
+
+    public void maybeSetCountFilteredUserTyping(int countFilteredUserTyping) {
+        mEventInternal.ifPresent(event -> {
+            event.mCountFilteredUserTyping = countFilteredUserTyping;
+        });
+    }
+
+    public void maybeSetCountNotShownImePresentationNotDrawn(
+            int countNotShownImePresentationNotDrawn) {
+        mEventInternal.ifPresent(event -> {
+            event.mCountNotShownImePresentationNotDrawn = countNotShownImePresentationNotDrawn;
+        });
+    }
+
+    public void maybeSetCountNotShownImeUserNotSeen(int countNotShownImeUserNotSeen) {
+        mEventInternal.ifPresent(event -> {
+            event.mCountNotShownImeUserNotSeen = countNotShownImeUserNotSeen;
+        });
+    }
+
+    public void maybeSetDisplayPresentationType(@UiType int uiType) {
+        mEventInternal.ifPresent(event -> {
+            event.mDisplayPresentationType = getDisplayPresentationType(uiType);
+        });
+    }
+
+    public void logAndEndEvent() {
+        if (!mEventInternal.isPresent()) {
+            Slog.wtf(null, "Shouldn't be logging AutofillPresentationEventReported again for same "
+                    + "event");
+            return;
+        }
+        PresentationStatsEventInternal event = mEventInternal.get();
+        if (sVerbose) {
+            Slog.v(TAG, "Log AutofillPresentationEventReported:"
+                    + " requestId=" + event.mRequestId
+                    + " sessionId=" + mSessionId
+                    + " mNoPresentationEventReason=" + event.mNoPresentationReason
+                    + " mAvailableCount=" + event.mAvailableCount
+                    + " mCountShown=" + event.mCountShown
+                    + " mCountFilteredUserTyping=" + event.mCountFilteredUserTyping
+                    + " mCountNotShownImePresentationNotDrawn="
+                    + event.mCountNotShownImePresentationNotDrawn
+                    + " mCountNotShownImeUserNotSeen=" + event.mCountNotShownImeUserNotSeen
+                    + " mDisplayPresentationType=" + event.mDisplayPresentationType);
+        }
+
+        // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
+        if (!event.mIsDatasetAvailable) {
+            mEventInternal = Optional.empty();
+            return;
+        }
+        FrameworkStatsLog.write(
+                AUTOFILL_PRESENTATION_EVENT_REPORTED,
+                event.mRequestId,
+                mSessionId,
+                event.mNoPresentationReason,
+                event.mAvailableCount,
+                event.mCountShown,
+                event.mCountFilteredUserTyping,
+                event.mCountNotShownImePresentationNotDrawn,
+                event.mCountNotShownImeUserNotSeen,
+                event.mDisplayPresentationType);
+        mEventInternal = Optional.empty();
+    }
+
+    private final class PresentationStatsEventInternal {
+        int mRequestId;
+        @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
+        boolean mIsDatasetAvailable;
+        int mAvailableCount;
+        int mCountShown;
+        int mCountFilteredUserTyping;
+        int mCountNotShownImePresentationNotDrawn;
+        int mCountNotShownImeUserNotSeen;
+        int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+
+        PresentationStatsEventInternal() {}
+    }
+
+    static int getNoPresentationEventReason(
+            @AutofillManager.AutofillCommitReason int commitReason) {
+        switch (commitReason) {
+            case COMMIT_REASON_VIEW_COMMITTED:
+                return NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY;
+            case COMMIT_REASON_ACTIVITY_FINISHED:
+                return NOT_SHOWN_REASON_ACTIVITY_FINISHED;
+            case COMMIT_REASON_VIEW_CHANGED:
+                return NOT_SHOWN_REASON_VIEW_CHANGED;
+            case COMMIT_REASON_VIEW_CLICKED:
+                // TODO(b/234185326): Add separate reason for view clicked.
+            default:
+                return NOT_SHOWN_REASON_UNKNOWN;
+        }
+    }
+
+    private static int getDisplayPresentationType(@UiType int uiType) {
+        switch (uiType) {
+            case UI_TYPE_MENU:
+                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
+            case UI_TYPE_INLINE:
+                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+            case UI_TYPE_DIALOG:
+                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
+            default:
+                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ca0a780..0fe9f8f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -32,6 +32,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -42,6 +43,9 @@
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_CHANGED;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
@@ -107,6 +111,7 @@
 import android.view.KeyEvent;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.AutofillCommitReason;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
@@ -391,6 +396,10 @@
                 }
             };
 
+    @NonNull
+    @GuardedBy("mLock")
+    private PresentationStatsEventLogger mPresentationStatsEventLogger;
+
     void onSwitchInputMethodLocked() {
         // One caveat is that for the case where the focus is on a field for which regular autofill
         // returns null, and augmented autofill is triggered,  and then the user switches the input
@@ -924,6 +933,7 @@
             Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
                     + ", flags=" + flags);
         }
+        mPresentationStatsEventLogger.maybeSetRequestId(requestId);
 
         // If the focus changes very quickly before the first request is returned each focus change
         // triggers a new partition and we end up with many duplicate partitions. This is
@@ -1021,6 +1031,7 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
         mSessionState = STATE_ACTIVE;
+        mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
@@ -1271,6 +1282,11 @@
                             message.length());
                 }
             }
+
+            // TODO(b/234185326): Add separate reason for failures.
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                    NOT_SHOWN_REASON_REQUEST_TIMEOUT);
+            mPresentationStatsEventLogger.logAndEndEvent();
         }
         notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED,
                 /* autofillableIds= */ null);
@@ -1859,7 +1875,8 @@
      */
     public void logContextCommitted() {
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
-                Event.NO_SAVE_UI_REASON_NONE));
+                Event.NO_SAVE_UI_REASON_NONE,
+                COMMIT_REASON_UNKNOWN));
     }
 
     /**
@@ -1867,13 +1884,16 @@
      * when necessary.
      *
      * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+     * @param commitReason The reason why context is committed.
      */
-    public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
+    public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason,
+            @AutofillCommitReason int commitReason) {
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
-                saveDialogNotShowReason));
+                saveDialogNotShowReason, commitReason));
     }
 
-    private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
+    private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
+            @AutofillCommitReason int commitReason) {
         final FillResponse lastResponse;
         synchronized (mLock) {
             lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -1903,28 +1923,35 @@
 
         // Sets field classification scores
         if (userData != null && fcStrategy != null) {
-            logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason);
+            logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason,
+                    commitReason);
         } else {
-            logContextCommitted(null, null, saveDialogNotShowReason);
+            logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
         }
     }
 
     private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
-            @NoSaveReason int saveDialogNotShowReason) {
+            @NoSaveReason int saveDialogNotShowReason,
+            @AutofillCommitReason int commitReason) {
         synchronized (mLock) {
             logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
-                    saveDialogNotShowReason);
+                    saveDialogNotShowReason, commitReason);
         }
     }
 
     @GuardedBy("mLock")
     private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
-            @NoSaveReason int saveDialogNotShowReason) {
+            @NoSaveReason int saveDialogNotShowReason,
+            @AutofillCommitReason int commitReason) {
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
 
+        mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
+        mPresentationStatsEventLogger.logAndEndEvent();
+
         final int flags = lastResponse.getFlags();
         if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
             if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): ignored by flags " + flags);
@@ -2102,7 +2129,8 @@
      */
     private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
             @NonNull FieldClassificationUserData userData,
-            @NoSaveReason int saveDialogNotShowReason) {
+            @NoSaveReason int saveDialogNotShowReason,
+            @AutofillCommitReason int commitReason) {
 
         final String[] userValues = userData.getValues();
         final String[] categoryIds = userData.getCategoryIds();
@@ -2148,7 +2176,7 @@
         final RemoteCallback callback = new RemoteCallback((result) -> {
             if (result == null) {
                 if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
-                logContextCommitted(null, null, saveDialogNotShowReason);
+                logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
                 return;
             }
             final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -2210,7 +2238,7 @@
             }
 
             logContextCommitted(detectedFieldIds, detectedFieldClassifications,
-                    saveDialogNotShowReason);
+                    saveDialogNotShowReason, commitReason);
         });
 
         fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
@@ -2891,6 +2919,9 @@
             if (sDebug) {
                 Slog.d(TAG, "Set the response has expired.");
             }
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                        NOT_SHOWN_REASON_VIEW_CHANGED);
+            mPresentationStatsEventLogger.logAndEndEvent();
             return;
         }
 
@@ -2936,6 +2967,7 @@
                 if (!isRequestSupportFillDialog(flags)) {
                     mSessionFlags.mFillDialogDisabled = true;
                 }
+                mPresentationStatsEventLogger.startNewEvent();
                 requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
                 break;
             case ACTION_VALUE_CHANGED:
@@ -2994,6 +3026,10 @@
                     return;
                 }
 
+                mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                        NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                mPresentationStatsEventLogger.logAndEndEvent();
+
                 if ((flags & FLAG_MANUAL_REQUEST) == 0) {
                     // Not a manual request
                     if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
@@ -3020,10 +3056,18 @@
                     }
                 }
 
+                mPresentationStatsEventLogger.startNewEvent();
                 if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
                     return;
                 }
 
+                if (viewState.getResponse() != null) {
+                    FillResponse response = viewState.getResponse();
+                    mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+                    mPresentationStatsEventLogger.maybeSetAvailableCount(
+                            response.getDatasets(), mCurrentViewId);
+                }
+
                 if (isSameViewEntered) {
                     setFillDialogDisabledAndStartInput();
                     return;
@@ -3042,6 +3086,10 @@
                     // on the IME side if it arrives before the input view is finished on the IME.
                     mInlineSessionController.resetInlineFillUiLocked();
                     mCurrentViewId = null;
+
+                    mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                                NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                    mPresentationStatsEventLogger.logAndEndEvent();
                 }
                 break;
             default:
@@ -3215,6 +3263,10 @@
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
                     mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
+
+                    mPresentationStatsEventLogger.maybeSetCountShown(
+                            response.getDatasets(), mCurrentViewId);
+                    mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_DIALOG);
                 }
                 // Just show fill dialog once, so disabled after shown.
                 // Note: Cannot disable before requestShowFillDialog() because the method
@@ -3232,9 +3284,15 @@
                 if (requestShowInlineSuggestionsLocked(response, filterText)) {
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_INLINE_SHOWN);
-                    //TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
+                    // TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
                     // rather than here where framework sends back the response.
                     mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE);
+
+                    // TODO(b/234475358): Log more accurate value of number of inline suggestions
+                    // shown, inflated, and filtered.
+                    mPresentationStatsEventLogger.maybeSetCountShown(
+                            response.getDatasets(), mCurrentViewId);
+                    mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_INLINE);
                     return;
                 }
             }
@@ -3246,6 +3304,10 @@
 
         synchronized (mLock) {
             mService.logDatasetShown(id, mClientState, UI_TYPE_MENU);
+
+            mPresentationStatsEventLogger.maybeSetCountShown(
+                    response.getDatasets(), mCurrentViewId);
+            mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU);
         }
 
         synchronized (mLock) {
@@ -3794,6 +3856,9 @@
         mResponses.put(requestId, newResponse);
         mClientState = newClientState != null ? newClientState : newResponse.getClientState();
 
+        mPresentationStatsEventLogger.maybeSetAvailableCount(
+                newResponse.getDatasets(), mCurrentViewId);
+
         setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
         updateFillDialogTriggerIdsLocked();
         updateTrackedIdsLocked();