Commit 959b0d0b authored by Michael Bai's avatar Michael Bai Committed by Chromium LUCI CQ

Autofill: Add metrics for server prediction

- Records if AwG is current Android autofill service.
- Records if and when the server type predication is available when
  feature is enabled.
- Records if AwG provides the suggestion and if user accepts the
  suggeston.

Bug: 1151542
Change-Id: I246aee27cdef757902351a5e43973bea2e6f15e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2612220
Commit-Queue: Michael Bai <michaelbai@chromium.org>
Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Reviewed-by: default avatarShimi Zhang <ctzsm@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842390}
parent 2af43d01
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.components.autofill; package org.chromium.components.autofill;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build; import android.os.Build;
...@@ -29,7 +30,8 @@ public class AutofillManagerWrapper { ...@@ -29,7 +30,8 @@ public class AutofillManagerWrapper {
// NOTE: As a result of the above, the tag below still references the name of this class from // NOTE: As a result of the above, the tag below still references the name of this class from
// when it was originally developed specifically for Android WebView. // when it was originally developed specifically for Android WebView.
public static final String TAG = "AwAutofillManager"; public static final String TAG = "AwAutofillManager";
private static final String AWG_COMPONENT_NAME =
"com.google.android.gms/com.google.android.gms.autofill.service.AutofillService";
/** /**
* The observer of suggestion window. * The observer of suggestion window.
*/ */
...@@ -58,17 +60,32 @@ public class AutofillManagerWrapper { ...@@ -58,17 +60,32 @@ public class AutofillManagerWrapper {
private boolean mDestroyed; private boolean mDestroyed;
private boolean mDisabled; private boolean mDisabled;
private ArrayList<WeakReference<InputUIObserver>> mInputUIObservers; private ArrayList<WeakReference<InputUIObserver>> mInputUIObservers;
// Indicates if AwG is the current Android autofill service.
private final boolean mIsAwGCurrentAutofillService;
public AutofillManagerWrapper(Context context) { public AutofillManagerWrapper(Context context) {
updateLogStat(); updateLogStat();
if (isLoggable()) log("constructor"); if (isLoggable()) log("constructor");
mAutofillManager = context.getSystemService(AutofillManager.class); mAutofillManager = context.getSystemService(AutofillManager.class);
mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled(); mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled();
if (mDisabled) { if (mDisabled) {
mIsAwGCurrentAutofillService = false;
if (isLoggable()) log("disabled"); if (isLoggable()) log("disabled");
return; return;
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ComponentName componentName = mAutofillManager.getAutofillServiceComponentName();
if (componentName != null) {
mIsAwGCurrentAutofillService =
AWG_COMPONENT_NAME.equals(componentName.flattenToString());
} else {
mIsAwGCurrentAutofillService = false;
}
} else {
mIsAwGCurrentAutofillService = false;
}
mMonitor = new AutofillInputUIMonitor(this); mMonitor = new AutofillInputUIMonitor(this);
mAutofillManager.registerCallback(mMonitor); mAutofillManager.registerCallback(mMonitor);
} }
...@@ -142,6 +159,14 @@ public class AutofillManagerWrapper { ...@@ -142,6 +159,14 @@ public class AutofillManagerWrapper {
return mDisabled; return mDisabled;
} }
/**
* Only work for Android P and beyond. Always return false for Android O.
* @return if the Autofill with Google is the current autofill service.
*/
public boolean isAwGCurrentAutofillService() {
return mIsAwGCurrentAutofillService;
}
private boolean checkAndWarnIfDestroyed() { private boolean checkAndWarnIfDestroyed() {
if (mDestroyed) { if (mDestroyed) {
Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper", Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper",
......
...@@ -319,7 +319,7 @@ public class AutofillProvider { ...@@ -319,7 +319,7 @@ public class AutofillProvider {
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
mAutofillManager = manager; mAutofillManager = manager;
mContainerView = containerView; mContainerView = containerView;
mAutofillUMA = new AutofillProviderUMA(context); mAutofillUMA = new AutofillProviderUMA(context, manager.isAwGCurrentAutofillService());
mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() { mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() {
@Override @Override
public void onInputUIShown() { public void onInputUIShown() {
...@@ -440,6 +440,9 @@ public class AutofillProvider { ...@@ -440,6 +440,9 @@ public class AutofillProvider {
int virtualId = mRequest.getVirtualId((short) focus); int virtualId = mRequest.getVirtualId((short) focus);
notifyVirtualViewEntered(mContainerView, virtualId, absBound); notifyVirtualViewEntered(mContainerView, virtualId, absBound);
mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled()); mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled());
if (hasServerPrediction) {
mAutofillUMA.onServerTypeAvailable(formData, /*afterSessionStarted=*/false);
}
mAutofillTriggeredTimeMillis = System.currentTimeMillis(); mAutofillTriggeredTimeMillis = System.currentTimeMillis();
mAutofillManager.notifyNewSessionStarted(); mAutofillManager.notifyNewSessionStarted();
...@@ -756,10 +759,12 @@ public class AutofillProvider { ...@@ -756,10 +759,12 @@ public class AutofillProvider {
@CalledByNative @CalledByNative
private void onQueryDone(boolean success) { private void onQueryDone(boolean success) {
mRequest.onQueryDone(success); mRequest.onQueryDone(success);
mAutofillUMA.onServerTypeAvailable(
success ? mRequest.mFormData : null, /*afterSessionStarted*/ true);
mAutofillManager.onQueryDone(success); mAutofillManager.onQueryDone(success);
} }
private static boolean isQueryServerFieldTypesEnabled() { public static boolean isQueryServerFieldTypesEnabled() {
if (sIsQueryServerFieldTypesEnabled == null) { if (sIsQueryServerFieldTypesEnabled == null) {
sIsQueryServerFieldTypesEnabled = sIsQueryServerFieldTypesEnabled =
AutofillProviderJni.get().isQueryServerFieldTypesEnabled(); AutofillProviderJni.get().isQueryServerFieldTypesEnabled();
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.components.autofill; package org.chromium.components.autofill;
import android.content.Context; import android.content.Context;
import android.os.Build;
import org.chromium.autofill.mojom.SubmissionSource; import org.chromium.autofill.mojom.SubmissionSource;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -26,6 +27,10 @@ public class AutofillProviderUMA { ...@@ -26,6 +27,10 @@ public class AutofillProviderUMA {
public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT = public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT =
"Autofill.WebView.CreatedByActivityContext"; "Autofill.WebView.CreatedByActivityContext";
// Records whether the current autofill service is AwG.
public static final String UMA_AUTOFILL_AWG_IS_CURRENT_SERVICE =
"Autofill.WebView.AwGIsCurrentService";
// Records what happened in an autofill session. // Records what happened in an autofill session.
public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession"; public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession";
// The possible value of UMA_AUTOFILL_AUTOFILL_SESSION. // The possible value of UMA_AUTOFILL_AUTOFILL_SESSION.
...@@ -45,6 +50,25 @@ public class AutofillProviderUMA { ...@@ -45,6 +50,25 @@ public class AutofillProviderUMA {
public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13; public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13;
public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14; public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14;
// The possible values for the server prediction availability.
public static final String UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY =
"Autofill.WebView.ServerPredicton.PredictionAvailability";
public static final int SERVER_PREDICTION_NOT_AVAILABLE = 0;
public static final int SERVER_PREDICTION_AVAILABLE_ON_SESSION_STARTS = 1;
public static final int SERVER_PREDICTION_AVAILABLE_AFTER_SESSION_STARTS = 2;
public static final int SERVER_PREDICTION_AVAILABLE_COUNT = 3;
// The possible values for the AwG suggestion availability.
public static final String UMA_AUTOFILL_AWG_SUGGSTION_AVAILABILITY =
"Autofill.WebView.ServerPrediction.AwGSuggestionAvailability";
public static final int AWG_NO_SUGGESTION = 0;
public static final int AWG_HAS_SUGGESTION_NO_AUTOFILL = 1;
public static final int AWG_HAS_SUGGESTION_AUTOFILLED = 2;
public static final int AWG_SUGGSTION_AVAILABLE_COUNT = 3;
public static final String UMA_AUTOFILL_VALID_SERVER_PREDICTION =
"Autofill.WebView.ServerPredicton.HasValidServerPrediction";
// Records whether user changed autofilled field if user ever changed the form. The action isn't // Records whether user changed autofilled field if user ever changed the form. The action isn't
// recorded if user didn't change form at all. // recorded if user didn't change form at all.
public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD = public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD =
...@@ -120,6 +144,31 @@ public class AutofillProviderUMA { ...@@ -120,6 +144,31 @@ public class AutofillProviderUMA {
if (mSuggestionTimeMillis != null) { if (mSuggestionTimeMillis != null) {
recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis); recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis);
} }
if (!mServerPredictionAvailable && AutofillProvider.isQueryServerFieldTypesEnabled()) {
RecordHistogram.recordEnumeratedHistogram(
UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY,
SERVER_PREDICTION_NOT_AVAILABLE, SERVER_PREDICTION_AVAILABLE_COUNT);
}
}
public void onServerTypeAvailable(FormData formData, boolean afterSessionStarted) {
if (!AutofillProvider.isQueryServerFieldTypesEnabled()) return;
mServerPredictionAvailable = true;
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY,
afterSessionStarted ? SERVER_PREDICTION_AVAILABLE_AFTER_SESSION_STARTS
: SERVER_PREDICTION_AVAILABLE_ON_SESSION_STARTS,
SERVER_PREDICTION_AVAILABLE_COUNT);
if (formData != null) {
boolean hasValidServerData = false;
for (FormFieldData fieldData : formData.mFields) {
if (!fieldData.getServerType().equals("NO_SERVER_DATA")) {
hasValidServerData = true;
break;
}
}
RecordHistogram.recordBooleanHistogram(
UMA_AUTOFILL_VALID_SERVER_PREDICTION, hasValidServerData);
}
} }
private int toUMAAutofillSessionValue() { private int toUMAAutofillSessionValue() {
...@@ -174,19 +223,62 @@ public class AutofillProviderUMA { ...@@ -174,19 +223,62 @@ public class AutofillProviderUMA {
private int mState; private int mState;
private Boolean mUserChangedAutofilledField; private Boolean mUserChangedAutofilledField;
// Indicates whether the server prediction arrives.
private boolean mServerPredictionAvailable;
}
/**
* The class to record Autofill.WebView.ServerPrediction.AwGSuggestion, is only instantiated
* when the Android platform AutofillServcie is AwG, This will give us more actual result in
* A/B experiment while only AwG supports the server prediction.
*/
private static class ServerPredictionRecorder {
private boolean mHasSuggestions;
private boolean mAutofilled;
private boolean mRecorded;
public void onSuggestionDisplayed() {
mHasSuggestions = true;
}
public void onAutofill() {
mAutofilled = true;
}
public void recordHistograms() {
if (mRecorded) return;
mRecorded = true;
int sample = AWG_NO_SUGGESTION;
if (mHasSuggestions) {
sample = mAutofilled ? AWG_HAS_SUGGESTION_AUTOFILLED
: AWG_HAS_SUGGESTION_NO_AUTOFILL;
}
RecordHistogram.recordEnumeratedHistogram(
UMA_AUTOFILL_AWG_SUGGSTION_AVAILABILITY, sample, AWG_SUGGSTION_AVAILABLE_COUNT);
}
} }
private SessionRecorder mRecorder; private SessionRecorder mRecorder;
private Boolean mAutofillDisabled; private Boolean mAutofillDisabled;
public AutofillProviderUMA(Context context) { private final boolean mIsAwGCurrentAutofillService;
private ServerPredictionRecorder mServerPredictionRecorder;
public AutofillProviderUMA(Context context, boolean isAwGCurrentAutofillService) {
RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT, RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT,
ContextUtils.activityFromContext(context) != null); ContextUtils.activityFromContext(context) != null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
RecordHistogram.recordBooleanHistogram(
UMA_AUTOFILL_AWG_IS_CURRENT_SERVICE, isAwGCurrentAutofillService);
}
mIsAwGCurrentAutofillService = isAwGCurrentAutofillService;
} }
public void onFormSubmitted(int submissionSource) { public void onFormSubmitted(int submissionSource) {
if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED); if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED);
recordSession(); recordSession();
if (mServerPredictionRecorder != null) mServerPredictionRecorder.recordHistograms();
// We record this no matter autofill service is disabled or not. // We record this no matter autofill service is disabled or not.
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE, RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE,
toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT); toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT);
...@@ -201,6 +293,9 @@ public class AutofillProviderUMA { ...@@ -201,6 +293,9 @@ public class AutofillProviderUMA {
if (mRecorder != null) recordSession(); if (mRecorder != null) recordSession();
mRecorder = new SessionRecorder(); mRecorder = new SessionRecorder();
if (mIsAwGCurrentAutofillService) {
mServerPredictionRecorder = new ServerPredictionRecorder();
}
} }
public void onVirtualStructureProvided() { public void onVirtualStructureProvided() {
...@@ -212,10 +307,12 @@ public class AutofillProviderUMA { ...@@ -212,10 +307,12 @@ public class AutofillProviderUMA {
mRecorder.record(SessionRecorder.EVENT_SUGGESTION_DISPLAYED); mRecorder.record(SessionRecorder.EVENT_SUGGESTION_DISPLAYED);
mRecorder.setSuggestionTimeMillis(suggestionTimeMillis); mRecorder.setSuggestionTimeMillis(suggestionTimeMillis);
} }
if (mServerPredictionRecorder != null) mServerPredictionRecorder.onSuggestionDisplayed();
} }
public void onAutofill() { public void onAutofill() {
if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED); if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED);
if (mServerPredictionRecorder != null) mServerPredictionRecorder.onAutofill();
} }
public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) { public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) {
...@@ -227,6 +324,17 @@ public class AutofillProviderUMA { ...@@ -227,6 +324,17 @@ public class AutofillProviderUMA {
} }
} }
/**
* Invoked when the server query was done or has arrived when the autofill sension starts.
*
* @param formData the form of the current session, is null if the query failed.
* @param afterSessionStarted true if the server type predication arrive after the session
* starts.
*/
public void onServerTypeAvailable(FormData formData, boolean afterSessionStarted) {
mRecorder.onServerTypeAvailable(formData, afterSessionStarted);
}
private void recordSession() { private void recordSession() {
if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) { if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) {
mRecorder.recordHistogram(); mRecorder.recordHistogram();
......
...@@ -71,9 +71,10 @@ JNI_AutofillProviderTestHelper_SimulateMainFrameAutofillServerResponseForTesting ...@@ -71,9 +71,10 @@ JNI_AutofillProviderTestHelper_SimulateMainFrameAutofillServerResponseForTesting
} }
if (found_fields_count > 0) { if (found_fields_count > 0) {
signatures = autofill::test::GetEncodedSignatures(*(j.second)); signatures = autofill::test::GetEncodedSignatures(*(j.second));
CHECK(found_fields_count == field_ids.size()); break;
} }
} }
CHECK(found_fields_count == field_ids.size());
std::string response_string; std::string response_string;
CHECK(response.SerializeToString(&response_string)); CHECK(response.SerializeToString(&response_string));
......
...@@ -4749,6 +4749,12 @@ others/histograms.xml --> ...@@ -4749,6 +4749,12 @@ others/histograms.xml -->
<int value="3" label="Initially incomplete &amp; Failure"/> <int value="3" label="Initially incomplete &amp; Failure"/>
</enum> </enum>
<enum name="AutofillAwGSuggestionAvailability">
<int value="0" label="No suggestion"/>
<int value="1" label="Has suggestion, user doesn't select it"/>
<int value="2" label="Has suggestion, user selects it"/>
</enum>
<enum name="AutofillCardholderNameFixFlowPromptEvent"> <enum name="AutofillCardholderNameFixFlowPromptEvent">
<int value="0" label="Shown"/> <int value="0" label="Shown"/>
<int value="1" label="Accepted"/> <int value="1" label="Accepted"/>
...@@ -5800,6 +5806,12 @@ others/histograms.xml --> ...@@ -5800,6 +5806,12 @@ others/histograms.xml -->
<int value="99" label="NOT_PASSWORD"/> <int value="99" label="NOT_PASSWORD"/>
</enum> </enum>
<enum name="AutofillServerPredictionAvailability">
<int value="0" label="Not available"/>
<int value="1" label="Available on session start"/>
<int value="2" label="Available after session start"/>
</enum>
<enum name="AutofillSessionStates"> <enum name="AutofillSessionStates">
<int value="0" label="Unknown"/> <int value="0" label="Unknown"/>
<int value="1" label="No callback from framework"/> <int value="1" label="No callback from framework"/>
...@@ -2019,6 +2019,17 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -2019,6 +2019,17 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<summary>Records the state of an autofill session.</summary> <summary>Records the state of an autofill session.</summary>
</histogram> </histogram>
<histogram name="Autofill.WebView.AwGIsCurrentService" enum="BooleanYesNo"
expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner>
<owner>src/android_webview/OWNERS</owner>
<summary>
Records whether Autofill with Google is the current Android autofill
service. It is recorded on AutofillProvider initialization. Only recorded in
Android P and beyond.
</summary>
</histogram>
<histogram name="Autofill.WebView.CreatedByActivityContext" <histogram name="Autofill.WebView.CreatedByActivityContext"
enum="BooleanEnabled" expires_after="2021-06-08"> enum="BooleanEnabled" expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner> <owner>michaelbai@chromium.org</owner>
...@@ -2035,6 +2046,42 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -2035,6 +2046,42 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary> </summary>
</histogram> </histogram>
<histogram name="Autofill.WebView.ServerPrediction.AwGSuggestionAvailability"
enum="AutofillAwGSuggestionAvailability" expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner>
<owner>src/android_webview/OWNERS</owner>
<summary>
Records whether Autofill with Google has suggestions and if the user selects
any suggestion. It is recorded when the form is submitted. Only recorded in
Android P and beyond.
</summary>
</histogram>
<histogram name="Autofill.WebView.ServerPredicton.HasValidServerPrediction"
enum="BooleanYesNo" expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner>
<owner>src/android_webview/OWNERS</owner>
<summary>
Records whether the server prediction of any field isn't NO_SERVER_DATA.
This histogram is recorded when the server prediction is available for the
current form, and only if AndroidAutofillQueryServerFieldTypes feature is
enabled.
</summary>
</histogram>
<histogram name="Autofill.WebView.ServerPredicton.PredictionAvailability"
enum="AutofillServerPredictionAvailability" expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner>
<owner>src/android_webview/OWNERS</owner>
<summary>
Records whether and when the server prediction response of current autofill
session is available even if query failed or there is no server data. This
histogram is recorded when the prediction becomes available or the new
session starts, and only if AndroidAutofillQueryServerFieldTypes feature is
enabled.
</summary>
</histogram>
<histogram name="Autofill.WebView.SubmissionSource" <histogram name="Autofill.WebView.SubmissionSource"
enum="AutofillSubmissionSource" expires_after="2021-06-08"> enum="AutofillSubmissionSource" expires_after="2021-06-08">
<owner>michaelbai@chromium.org</owner> <owner>michaelbai@chromium.org</owner>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment