diff --git a/core/api/current.txt b/core/api/current.txt index 3db1f3bf1f023..318fd35832283 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -38205,6 +38205,7 @@ package android.service.autofill { method public int getNoSaveUiReason(); method @NonNull public java.util.Set getSelectedDatasetIds(); method public int getType(); + method public int getUiType(); field public static final int NO_SAVE_UI_REASON_DATASET_MATCH = 6; // 0x6 field public static final int NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5 field public static final int NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3 @@ -38218,6 +38219,10 @@ 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 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 + field public static final int UI_TYPE_UNKNOWN = 0; // 0x0 } public final class FillRequest implements android.os.Parcelable { diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 7a8f6f415fbf3..0fb9f57f5f574 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -160,6 +160,7 @@ public final class FillEventHistory implements Parcelable { event.mDetectedFieldClassifications); } parcel.writeInt(event.mSaveDialogNotShowReason); + parcel.writeInt(event.mUiType); } } } @@ -278,6 +279,29 @@ public final class FillEventHistory implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface NoSaveReason{} + /** The autofill suggestion presentation is unknown, this will be set for the event + * that is unrelated to fill Ui presentation */ + public static final int UI_TYPE_UNKNOWN = 0; + + /** The autofill suggestion is shown as a menu popup presentation. */ + public static final int UI_TYPE_MENU = 1; + + /** The autofill suggestion is shown as a keyboard inline presentation. */ + public static final int UI_TYPE_INLINE = 2; + + /** The autofill suggestion is shown as a dialog presentation. */ + public static final int UI_TYPE_DIALOG = 3; + + /** @hide */ + @IntDef(prefix = { "UI_TYPE_" }, value = { + UI_TYPE_UNKNOWN, + UI_TYPE_MENU, + UI_TYPE_INLINE, + UI_TYPE_DIALOG + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UiType {} + @EventIds private final int mEventType; @Nullable private final String mDatasetId; @Nullable private final Bundle mClientState; @@ -298,6 +322,10 @@ public final class FillEventHistory implements Parcelable { @NoSaveReason private final int mSaveDialogNotShowReason; + + @UiType + private final int mUiType; + /** * Returns the type of the event. * @@ -497,6 +525,21 @@ public final class FillEventHistory implements Parcelable { return mSaveDialogNotShowReason; } + /** + * Returns fill suggestion ui presentation type which corresponds to types + * defined in {@link android.service.autofill.Presentations). + * + *

Note: Only set on events of type {@link #TYPE_DATASETS_SHOWN} and + * {@link #TYPE_DATASET_SELECTED}. For the other event types, the type is set to + * {@link #UI_TYPE_UNKNOWN }. + * + * @return The ui presentation type shown for user. + */ + @UiType + public int getUiType() { + return mUiType; + } + /** * Creates a new event. * @@ -573,6 +616,49 @@ public final class FillEventHistory implements Parcelable { @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, int saveDialogNotShowReason) { + this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, + changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, + manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, + saveDialogNotShowReason, UI_TYPE_UNKNOWN); + } + + /** + * Creates a new event. + * + * @param eventType The type of the event + * @param datasetId The dataset the event was on, or {@code null} if the event was on the + * whole response. + * @param clientState The client state associated with the event. + * @param selectedDatasetIds The ids of datasets selected by the user. + * @param ignoredDatasetIds The ids of datasets NOT select by the user. + * @param changedFieldIds The ids of fields changed by the user. + * @param changedDatasetIds The ids of the datasets that havd values matching the + * respective entry on {@code changedFieldIds}. + * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user + * and belonged to datasets. + * @param manuallyFilledDatasetIds The ids of datasets that had values matching the + * respective entry on {@code manuallyFilledFieldIds}. + * @param detectedFieldClassifications the field classification matches. + * @param saveDialogNotShowReason The reason why a save dialog was not shown. + * @param uiType The ui presentation type for fill suggestion. + * + * @throws IllegalArgumentException If the length of {@code changedFieldIds} and + * {@code changedDatasetIds} doesn't match. + * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and + * {@code manuallyFilledDatasetIds} doesn't match. + * + * @hide + */ + public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, + @Nullable List selectedDatasetIds, + @Nullable ArraySet ignoredDatasetIds, + @Nullable ArrayList changedFieldIds, + @Nullable ArrayList changedDatasetIds, + @Nullable ArrayList manuallyFilledFieldIds, + @Nullable ArrayList> manuallyFilledDatasetIds, + @Nullable AutofillId[] detectedFieldIds, + @Nullable FieldClassification[] detectedFieldClassifications, + int saveDialogNotShowReason, int uiType) { mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN, "eventType"); mDatasetId = datasetId; @@ -581,16 +667,16 @@ public final class FillEventHistory implements Parcelable { mIgnoredDatasetIds = ignoredDatasetIds; if (changedFieldIds != null) { Preconditions.checkArgument(!ArrayUtils.isEmpty(changedFieldIds) - && changedDatasetIds != null - && changedFieldIds.size() == changedDatasetIds.size(), + && changedDatasetIds != null + && changedFieldIds.size() == changedDatasetIds.size(), "changed ids must have same length and not be empty"); } mChangedFieldIds = changedFieldIds; mChangedDatasetIds = changedDatasetIds; if (manuallyFilledFieldIds != null) { Preconditions.checkArgument(!ArrayUtils.isEmpty(manuallyFilledFieldIds) - && manuallyFilledDatasetIds != null - && manuallyFilledFieldIds.size() == manuallyFilledDatasetIds.size(), + && manuallyFilledDatasetIds != null + && manuallyFilledFieldIds.size() == manuallyFilledDatasetIds.size(), "manually filled ids must have same length and not be empty"); } mManuallyFilledFieldIds = manuallyFilledFieldIds; @@ -602,12 +688,14 @@ public final class FillEventHistory implements Parcelable { mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason, NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH, "saveDialogNotShowReason"); + mUiType = uiType; } @Override public String toString() { return "FillEvent [datasetId=" + mDatasetId - + ", type=" + mEventType + + ", type=" + eventToString(mEventType) + + ", uiType=" + uiTypeToString(mUiType) + ", selectedDatasets=" + mSelectedDatasetIds + ", ignoredDatasetIds=" + mIgnoredDatasetIds + ", changedFieldIds=" + mChangedFieldIds @@ -620,6 +708,38 @@ public final class FillEventHistory implements Parcelable { + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; } + + private static String eventToString(int eventType) { + switch (eventType) { + case TYPE_DATASET_SELECTED: + return "TYPE_DATASET_SELECTED"; + case TYPE_DATASET_AUTHENTICATION_SELECTED: + return "TYPE_DATASET_AUTHENTICATION_SELECTED"; + case TYPE_AUTHENTICATION_SELECTED: + return "TYPE_AUTHENTICATION_SELECTED"; + case TYPE_SAVE_SHOWN: + return "TYPE_SAVE_SHOWN"; + case TYPE_CONTEXT_COMMITTED: + return "TYPE_CONTEXT_COMMITTED"; + case TYPE_DATASETS_SHOWN: + return "TYPE_DATASETS_SHOWN"; + default: + return "TYPE_UNKNOWN"; + } + } + + private static String uiTypeToString(int uiType) { + switch (uiType) { + case UI_TYPE_MENU: + return "UI_TYPE_MENU"; + case UI_TYPE_INLINE: + return "UI_TYPE_INLINE"; + case UI_TYPE_DIALOG: + return "UI_TYPE_FILL_DIALOG"; + default: + return "UI_TYPE_UNKNOWN"; + } + } } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -660,13 +780,14 @@ public final class FillEventHistory implements Parcelable { ? FieldClassification.readArrayFromParcel(parcel) : null; final int saveDialogNotShowReason = parcel.readInt(); + final int uiType = parcel.readInt(); selection.addEvent(new Event(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason)); + saveDialogNotShowReason, uiType)); } return selection; } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 1cff3744687ef..8c4db70c301db 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -16,6 +16,8 @@ package com.android.server.autofill; +import static android.service.autofill.FillEventHistory.Event.NO_SAVE_UI_REASON_NONE; +import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED; @@ -816,12 +818,13 @@ final class AutofillManagerServiceImpl /** * Updates the last fill response when a dataset is shown. */ - void logDatasetShown(int sessionId, @Nullable Bundle clientState) { + void logDatasetShown(int sessionId, @Nullable Bundle clientState, int presentationType) { synchronized (mLock) { if (isValidEventLocked("logDatasetShown", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, - null, null, null, null, null)); + null, null, null, null, null, NO_SAVE_UI_REASON_NONE, + presentationType)); } } } @@ -858,9 +861,12 @@ final class AutofillManagerServiceImpl || mAugmentedAutofillEventHistory.getSessionId() != sessionId) { return; } + // Augmented Autofill only logs for inline now, so set UI_TYPE_INLINE here. + // Ideally should not hardcode here and should also log for menu presentation. mAugmentedAutofillEventHistory.addEvent( new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, - null, null, null, null, null)); + null, null, null, null, null, NO_SAVE_UI_REASON_NONE, + UI_TYPE_INLINE)); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0d334e5882146..54183c98204ea 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,9 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE; +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.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; @@ -3290,7 +3293,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { final ViewState currentView = mViewStates.get(mCurrentViewId); currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN); - mService.logDatasetShown(id, mClientState); + mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG); } return; } @@ -3304,7 +3307,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.setState(ViewState.STATE_INLINE_SHOWN); //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, // rather than here where framework sends back the response. - mService.logDatasetShown(id, mClientState); + mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE); return; } } @@ -3314,7 +3317,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.getServicePackageName(), mComponentName, targetLabel, targetIcon, this, id, mCompatMode); - mService.logDatasetShown(id, mClientState); + synchronized (mLock) { + mService.logDatasetShown(id, mClientState, UI_TYPE_MENU); + } synchronized (mLock) { if (mUiShownTime == 0) {