Merge "Log why presentation was not shown" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-06-01 08:04:53 +00:00
committed by Android (Google) Code Review
6 changed files with 409 additions and 29 deletions

View File

@@ -521,6 +521,61 @@ public final class AutofillManager {
/** @hide */ /** @hide */
public static final int RESULT_CODE_NOT_SERVICE = -1; 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. * Makes an authentication id from a request id and a dataset id.
* *
@@ -1585,7 +1640,7 @@ public final class AutofillManager {
} }
if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) { if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
if (sDebug) Log.d(TAG, "triggering commit by click of " + 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)); mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
} }
} }
@@ -1603,7 +1658,7 @@ public final class AutofillManager {
synchronized (mLock) { synchronized (mLock) {
if (mSaveOnFinish) { if (mSaveOnFinish) {
if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()"); if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
commitLocked(); commitLocked(/* commitReason= */ COMMIT_REASON_ACTIVITY_FINISHED);
} else { } else {
if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()"); if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
cancelLocked(); cancelLocked();
@@ -1628,16 +1683,16 @@ public final class AutofillManager {
} }
if (sVerbose) Log.v(TAG, "commit() called by app"); if (sVerbose) Log.v(TAG, "commit() called by app");
synchronized (mLock) { synchronized (mLock) {
commitLocked(); commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_COMMITTED);
} }
} }
@GuardedBy("mLock") @GuardedBy("mLock")
private void commitLocked() { private void commitLocked(@AutofillCommitReason int commitReason) {
if (!mEnabled && !isActiveLocked()) { if (!mEnabled && !isActiveLocked()) {
return; return;
} }
finishSessionLocked(); finishSessionLocked(/* commitReason= */ commitReason);
} }
/** /**
@@ -2070,13 +2125,13 @@ public final class AutofillManager {
} }
@GuardedBy("mLock") @GuardedBy("mLock")
private void finishSessionLocked() { private void finishSessionLocked(@AutofillCommitReason int commitReason) {
if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked()); if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
if (!isActiveLocked()) return; if (!isActiveLocked()) return;
try { try {
mService.finishSession(mSessionId, mContext.getUserId()); mService.finishSession(mSessionId, mContext.getUserId(), commitReason);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
@@ -3549,7 +3604,7 @@ public final class AutofillManager {
} }
if (mVisibleTrackedIds == null) { if (mVisibleTrackedIds == null) {
finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
} }
} }
@@ -3582,9 +3637,9 @@ public final class AutofillManager {
if (mVisibleTrackedIds == null) { if (mVisibleTrackedIds == null) {
if (sVerbose) { 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) { if (sVerbose) {
Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
} }
finishSessionLocked(); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
} }
} }
} }

View File

@@ -49,7 +49,7 @@ oneway interface IAutoFillManager {
void updateSession(int sessionId, in AutofillId id, in Rect bounds, void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId); in AutofillValue value, int action, int flags, int userId);
void setAutofillFailure(int sessionId, in List<AutofillId> ids, 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 cancelSession(int sessionId, int userId);
void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId); void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
void setHasCallback(int sessionId, int userId, boolean hasIt); void setHasCallback(int sessionId, int userId, boolean hasIt);

View File

@@ -69,6 +69,7 @@ import android.util.SparseBooleanArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.view.autofill.AutofillId; import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillManagerInternal; import android.view.autofill.AutofillManagerInternal;
import android.view.autofill.AutofillValue; import android.view.autofill.AutofillValue;
@@ -1657,11 +1658,12 @@ public final class AutofillManagerService
} }
@Override @Override
public void finishSession(int sessionId, int userId) { public void finishSession(int sessionId, int userId,
@AutofillCommitReason int commitReason) {
synchronized (mLock) { synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) { if (service != null) {
service.finishSessionLocked(sessionId, getCallingUid()); service.finishSessionLocked(sessionId, getCallingUid(), commitReason);
} else if (sVerbose) { } else if (sVerbose) {
Slog.v(TAG, "finishSession(): no service for " + userId); Slog.v(TAG, "finishSession(): no service for " + userId);
} }

View File

@@ -70,6 +70,7 @@ import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.autofill.AutofillId; import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillValue; import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutoFillManagerClient;
@@ -423,7 +424,7 @@ final class AutofillManagerServiceImpl
} }
@GuardedBy("mLock") @GuardedBy("mLock")
void finishSessionLocked(int sessionId, int uid) { void finishSessionLocked(int sessionId, int uid, @AutofillCommitReason int commitReason) {
if (!isEnabledLocked()) { if (!isEnabledLocked()) {
return; return;
} }
@@ -438,7 +439,7 @@ final class AutofillManagerServiceImpl
final Session.SaveResult saveResult = session.showSaveLocked(); final Session.SaveResult saveResult = session.showSaveLocked();
session.logContextCommitted(saveResult.getNoSaveUiReason()); session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason);
if (saveResult.isLogSaveShown()) { if (saveResult.isLogSaveShown()) {
session.logSaveUiShown(); session.logSaveUiShown();

View File

@@ -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;
}
}
}

View File

@@ -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_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; 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.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; 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.sDebug;
import static com.android.server.autofill.Helper.sVerbose; import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray; 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_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; 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.KeyEvent;
import android.view.autofill.AutofillId; import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillValue; import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutoFillManagerClient;
@@ -391,6 +396,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
}; };
@NonNull
@GuardedBy("mLock")
private PresentationStatsEventLogger mPresentationStatsEventLogger;
void onSwitchInputMethodLocked() { void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill // 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 // 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 Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
+ ", flags=" + flags); + ", flags=" + flags);
} }
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
// If the focus changes very quickly before the first request is returned each focus change // 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 // 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; mComponentName = componentName;
mCompatMode = compatMode; mCompatMode = compatMode;
mSessionState = STATE_ACTIVE; mSessionState = STATE_ACTIVE;
mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
synchronized (mLock) { synchronized (mLock) {
mSessionFlags = new SessionFlags(); mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
@@ -1271,6 +1282,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
message.length()); message.length());
} }
} }
// TODO(b/234185326): Add separate reason for failures.
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
NOT_SHOWN_REASON_REQUEST_TIMEOUT);
mPresentationStatsEventLogger.logAndEndEvent();
} }
notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED,
/* autofillableIds= */ null); /* autofillableIds= */ null);
@@ -1859,7 +1875,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/ */
public void logContextCommitted() { public void logContextCommitted() {
mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, 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. * when necessary.
* *
* @param saveDialogNotShowReason The reason why a save dialog was not shown. * @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, 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; final FillResponse lastResponse;
synchronized (mLock) { synchronized (mLock) {
lastResponse = getLastResponseLocked("logContextCommited(%s)"); lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -1903,28 +1923,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Sets field classification scores // Sets field classification scores
if (userData != null && fcStrategy != null) { if (userData != null && fcStrategy != null) {
logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason); logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason,
commitReason);
} else { } else {
logContextCommitted(null, null, saveDialogNotShowReason); logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
} }
} }
private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
@Nullable ArrayList<FieldClassification> detectedFieldClassifications, @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
@NoSaveReason int saveDialogNotShowReason) { @NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
synchronized (mLock) { synchronized (mLock) {
logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
saveDialogNotShowReason); saveDialogNotShowReason, commitReason);
} }
} }
@GuardedBy("mLock") @GuardedBy("mLock")
private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
@Nullable ArrayList<FieldClassification> detectedFieldClassifications, @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
@NoSaveReason int saveDialogNotShowReason) { @NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
if (lastResponse == null) return; if (lastResponse == null) return;
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
mPresentationStatsEventLogger.logAndEndEvent();
final int flags = lastResponse.getFlags(); final int flags = lastResponse.getFlags();
if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) { if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): ignored by flags " + flags); 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, private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
@NonNull FieldClassificationUserData userData, @NonNull FieldClassificationUserData userData,
@NoSaveReason int saveDialogNotShowReason) { @NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
final String[] userValues = userData.getValues(); final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds(); final String[] categoryIds = userData.getCategoryIds();
@@ -2148,7 +2176,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final RemoteCallback callback = new RemoteCallback((result) -> { final RemoteCallback callback = new RemoteCallback((result) -> {
if (result == null) { if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
logContextCommitted(null, null, saveDialogNotShowReason); logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
return; return;
} }
final Scores scores = result.getParcelable(EXTRA_SCORES); final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -2210,7 +2238,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
logContextCommitted(detectedFieldIds, detectedFieldClassifications, logContextCommitted(detectedFieldIds, detectedFieldClassifications,
saveDialogNotShowReason); saveDialogNotShowReason, commitReason);
}); });
fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
@@ -2891,6 +2919,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) { if (sDebug) {
Slog.d(TAG, "Set the response has expired."); Slog.d(TAG, "Set the response has expired.");
} }
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
NOT_SHOWN_REASON_VIEW_CHANGED);
mPresentationStatsEventLogger.logAndEndEvent();
return; return;
} }
@@ -2936,6 +2967,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (!isRequestSupportFillDialog(flags)) { if (!isRequestSupportFillDialog(flags)) {
mSessionFlags.mFillDialogDisabled = true; mSessionFlags.mFillDialogDisabled = true;
} }
mPresentationStatsEventLogger.startNewEvent();
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break; break;
case ACTION_VALUE_CHANGED: case ACTION_VALUE_CHANGED:
@@ -2994,6 +3026,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return; return;
} }
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
mPresentationStatsEventLogger.logAndEndEvent();
if ((flags & FLAG_MANUAL_REQUEST) == 0) { if ((flags & FLAG_MANUAL_REQUEST) == 0) {
// Not a manual request // Not a manual request
if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains( if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
@@ -3020,10 +3056,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
} }
mPresentationStatsEventLogger.startNewEvent();
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) { if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
return; return;
} }
if (viewState.getResponse() != null) {
FillResponse response = viewState.getResponse();
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
mPresentationStatsEventLogger.maybeSetAvailableCount(
response.getDatasets(), mCurrentViewId);
}
if (isSameViewEntered) { if (isSameViewEntered) {
setFillDialogDisabledAndStartInput(); setFillDialogDisabledAndStartInput();
return; 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. // on the IME side if it arrives before the input view is finished on the IME.
mInlineSessionController.resetInlineFillUiLocked(); mInlineSessionController.resetInlineFillUiLocked();
mCurrentViewId = null; mCurrentViewId = null;
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
mPresentationStatsEventLogger.logAndEndEvent();
} }
break; break;
default: default:
@@ -3215,6 +3263,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ViewState currentView = mViewStates.get(mCurrentViewId); final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN); currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG); 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. // Just show fill dialog once, so disabled after shown.
// Note: Cannot disable before requestShowFillDialog() because the method // Note: Cannot disable before requestShowFillDialog() because the method
@@ -3232,9 +3284,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestShowInlineSuggestionsLocked(response, filterText)) { if (requestShowInlineSuggestionsLocked(response, filterText)) {
final ViewState currentView = mViewStates.get(mCurrentViewId); final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN); 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. // rather than here where framework sends back the response.
mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE); 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; return;
} }
} }
@@ -3246,6 +3304,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) { synchronized (mLock) {
mService.logDatasetShown(id, mClientState, UI_TYPE_MENU); mService.logDatasetShown(id, mClientState, UI_TYPE_MENU);
mPresentationStatsEventLogger.maybeSetCountShown(
response.getDatasets(), mCurrentViewId);
mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU);
} }
synchronized (mLock) { synchronized (mLock) {
@@ -3794,6 +3856,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mResponses.put(requestId, newResponse); mResponses.put(requestId, newResponse);
mClientState = newClientState != null ? newClientState : newResponse.getClientState(); mClientState = newClientState != null ? newClientState : newResponse.getClientState();
mPresentationStatsEventLogger.maybeSetAvailableCount(
newResponse.getDatasets(), mCurrentViewId);
setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false); setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
updateFillDialogTriggerIdsLocked(); updateFillDialogTriggerIdsLocked();
updateTrackedIdsLocked(); updateTrackedIdsLocked();