Loading core/java/android/view/autofill/AutofillManager.java +66 −11 Original line number Diff line number Diff line Loading @@ -536,6 +536,61 @@ public final class AutofillManager { /** @hide */ 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. * Loading Loading @@ -1605,7 +1660,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)); } } Loading @@ -1623,7 +1678,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(); Loading @@ -1648,16 +1703,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); } /** Loading Loading @@ -2123,13 +2178,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(); } Loading Loading @@ -3624,7 +3679,7 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } Loading Loading @@ -3657,9 +3712,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); } } Loading Loading @@ -3731,7 +3786,7 @@ public final class AutofillManager { if (sVerbose) { Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); } finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } } Loading core/java/android/view/autofill/IAutoFillManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading services/autofill/java/com/android/server/autofill/AutofillManagerService.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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(); Loading services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java 0 → 100644 +257 −0 Original line number Diff line number Diff line /* * 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; } } } Loading
core/java/android/view/autofill/AutofillManager.java +66 −11 Original line number Diff line number Diff line Loading @@ -536,6 +536,61 @@ public final class AutofillManager { /** @hide */ 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. * Loading Loading @@ -1605,7 +1660,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)); } } Loading @@ -1623,7 +1678,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(); Loading @@ -1648,16 +1703,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); } /** Loading Loading @@ -2123,13 +2178,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(); } Loading Loading @@ -3624,7 +3679,7 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } Loading Loading @@ -3657,9 +3712,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); } } Loading Loading @@ -3731,7 +3786,7 @@ public final class AutofillManager { if (sVerbose) { Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); } finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); } } } Loading
core/java/android/view/autofill/IAutoFillManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
services/autofill/java/com/android/server/autofill/AutofillManagerService.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading
services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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(); Loading
services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java 0 → 100644 +257 −0 Original line number Diff line number Diff line /* * 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; } } }