Add a new Autofill FillEvent - for when the user taps a field.

Test: atest
android.autofillservice.cts.inline.InlineFillEventHistoryTest

atest android.autofillservice.cts.dropdown.FillEventHistoryTest

Bug: 266231634

Change-Id: I96d5aea6a0e8d834ffda5eb593f9969a7b1826e8
Merged-In: I96d5aea6a0e8d834ffda5eb593f9969a7b1826e8
This commit is contained in:
Tim Yu
2023-02-09 22:44:46 +00:00
parent dc7f62fd11
commit 84484c133e
4 changed files with 92 additions and 3 deletions

View File

@@ -39861,6 +39861,7 @@ package android.service.autofill {
field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1

View File

@@ -233,6 +233,22 @@ public final class FillEventHistory implements Parcelable {
*/
public static final int TYPE_DATASETS_SHOWN = 5;
/**
* The app/user requested for a field to be Autofilled.
*
* This event is fired when the view has been entered (by user or app) in order
* to differentiate from FillRequests that have been pretriggered for FillDialogs.
*
* For example, the user might navigate away from a screen without tapping any
* fields. In this case, a FillRequest/FillResponse has been generated, but was
* not used for Autofilling. The user did not intend to see an Autofill result,
* but a FillRequest was still generated. This is different from when the user
* did tap on a field after the pretriggered FillRequest, this event will appear
* in the FillEventHistory, signaling that the user did intend to Autofill
* something.
*/
public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6;
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_DATASET_SELECTED,
@@ -240,7 +256,8 @@ public final class FillEventHistory implements Parcelable {
TYPE_AUTHENTICATION_SELECTED,
TYPE_SAVE_SHOWN,
TYPE_CONTEXT_COMMITTED,
TYPE_DATASETS_SHOWN
TYPE_DATASETS_SHOWN,
TYPE_VIEW_REQUESTED_AUTOFILL
})
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
@@ -659,8 +676,8 @@ public final class FillEventHistory implements Parcelable {
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications,
int saveDialogNotShowReason, int uiType) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,
"eventType");
mEventType = Preconditions.checkArgumentInRange(eventType, 0,
TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
mDatasetId = datasetId;
mClientState = clientState;
mSelectedDatasetIds = selectedDatasetIds;
@@ -723,6 +740,8 @@ public final class FillEventHistory implements Parcelable {
return "TYPE_CONTEXT_COMMITTED";
case TYPE_DATASETS_SHOWN:
return "TYPE_DATASETS_SHOWN";
case TYPE_VIEW_REQUESTED_AUTOFILL:
return "TYPE_VIEW_REQUESTED_AUTOFILL";
default:
return "TYPE_UNKNOWN";
}

View File

@@ -849,6 +849,32 @@ final class AutofillManagerServiceImpl
}
}
/**
* Updates the last fill response when a view was entered.
*/
void logViewEntered(int sessionId, @Nullable Bundle clientState) {
synchronized (mLock) {
if (!isValidEventLocked("logViewEntered", sessionId)) {
return;
}
if (mEventHistory.getEvents() != null) {
// Do not log this event more than once
for (Event event : mEventHistory.getEvents()) {
if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
return;
}
}
}
mEventHistory.addEvent(
new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
null, null, null, null, null, null, null));
}
}
void logAugmentedAutofillAuthenticationSelected(int sessionId, @Nullable String selectedDataset,
@Nullable Bundle clientState) {
synchronized (mLock) {

View File

@@ -441,6 +441,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private boolean mPreviouslyFillDialogPotentiallyStarted;
/**
* Keeps track of if the user entered view, this is used to
* distinguish Fill Request that did not have user interaction
* with ones that did.
*
* This is set to true when entering view - after FillDialog FillRequest
* or on plain user tap.
*/
@NonNull
@GuardedBy("mLock")
private boolean mLogViewEntered;
/**
* Keeps the fill dialog trigger ids of the last response. This invalidates
* the trigger ids of the previous response.
@@ -1289,6 +1301,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
mLogViewEntered = false;
}
/**
@@ -1413,6 +1426,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.setLastResponse(id, response);
synchronized (mLock) {
if (mLogViewEntered) {
mLogViewEntered = false;
mService.logViewEntered(id, null);
}
}
final long disableDuration = response.getDisableDuration();
final boolean autofillDisabled = disableDuration > 0;
if (autofillDisabled) {
@@ -3545,6 +3566,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
synchronized (mLock) {
if (!mLogViewEntered) {
// If the current request is for FillDialog (preemptive)
// then this is the first time that the view is entered
// (mLogViewEntered == false) in this case, setLastResponse()
// has already been called, so just log here.
// If the current request is not and (mLogViewEntered == false)
// then the last session is being tracked (setLastResponse not called)
// so this calling logViewEntered will be a nop.
// Calling logViewEntered() twice will only log it once
// TODO(271181979): this is broken for multiple partitions
mService.logViewEntered(this.id, null);
}
// If this is the first time view is entered for inline, the last
// session is still being tracked, so logViewEntered() needs
// to be delayed until setLastResponse is called.
// For fill dialog requests case logViewEntered is already called above
// so this will do nothing. Assumption: only one fill dialog per session
mLogViewEntered = true;
}
// Previously, fill request will only start whenever a view is entered.
// With Fill Dialog, request starts prior to view getting entered. So, we can't end
// the event at this moment, otherwise we will be wrongly attributing fill dialog