diff options
6 files changed, 409 insertions, 29 deletions
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 7353bbccf77c..7d7270d1ef76 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -522,6 +522,61 @@ public final class AutofillManager { 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 @@ public final class AutofillManager { } 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 @@ public final class AutofillManager { 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 @@ public final class AutofillManager { } 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 @@ public final class AutofillManager { } @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 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { - finishSessionLocked(); + finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } @@ -3582,9 +3637,9 @@ public final class AutofillManager { 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 @@ public final class AutofillManager { 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 a507e74c30ab..cefd6dc13005 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -49,7 +49,7 @@ oneway interface IAutoFillManager { 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 fc95cdd53f29..15d3fa9a03c4 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.SparseBooleanArray; 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 @@ public final class AutofillManagerService } @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 aa5c501cd94e..fe85db286fa8 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.Slog; 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 @@ final class AutofillManagerServiceImpl } @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 class AutofillManagerServiceImpl 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 000000000000..c6e595b1d785 --- /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 ca0a7806898c..0fe9f8f90cea 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_START_SESSION; 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.getNumericValue; 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.util.TimeUtils; 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } }; + @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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName = componentName; mCompatMode = compatMode; mSessionState = STATE_ACTIVE; + mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId); synchronized (mLock) { mSessionFlags = new SessionFlags(); mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; @@ -1271,6 +1282,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ 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 class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } logContextCommitted(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason); + saveDialogNotShowReason, commitReason); }); fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, @@ -2891,6 +2919,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) { Slog.d(TAG, "Set the response has expired."); } + mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + NOT_SHOWN_REASON_VIEW_CHANGED); + mPresentationStatsEventLogger.logAndEndEvent(); return; } @@ -2936,6 +2967,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!isRequestSupportFillDialog(flags)) { mSessionFlags.mFillDialogDisabled = true; } + mPresentationStatsEventLogger.startNewEvent(); requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); break; case ACTION_VALUE_CHANGED: @@ -2994,6 +3026,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 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 class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mResponses.put(requestId, newResponse); mClientState = newClientState != null ? newClientState : newResponse.getClientState(); + mPresentationStatsEventLogger.maybeSetAvailableCount( + newResponse.getDatasets(), mCurrentViewId); + setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false); updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); |