• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.autofill;
18 
19 import static android.service.autofill.Flags.FLAG_AUTOFILL_W_METRICS;
20 import static android.view.autofill.Helper.sVerbose;
21 
22 import android.annotation.FlaggedApi;
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.IntentSender;
27 import android.os.Bundle;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.ArrayMap;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.view.autofill.AutofillId;
34 import android.view.autofill.AutofillManager;
35 
36 import com.android.internal.util.ArrayUtils;
37 import com.android.internal.util.Preconditions;
38 
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 
48 /**
49  * Describes what happened after the last
50  * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
51  * call.
52  *
53  * <p>This history is typically used to keep track of previous user actions to optimize further
54  * requests. For example, the service might return email addresses in alphabetical order by
55  * default, but change that order based on the address the user picked on previous requests.
56  *
57  * <p>The history is not persisted over reboots, and it's cleared every time the service
58  * replies to a
59  * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
60  * by calling {@link FillCallback#onSuccess(FillResponse)} or
61  * {@link FillCallback#onFailure(CharSequence)} (if the service doesn't call any of these methods,
62  * the history will clear out after some pre-defined time).
63  */
64 public final class FillEventHistory implements Parcelable {
65     private static final String TAG = "FillEventHistory";
66 
67     /**
68      * The ID of the autofill session that created the {@link FillResponse}.
69      *
70      * TODO: add this to the parcel.
71      */
72     private final int mSessionId;
73 
74     @Nullable private final Bundle mClientState;
75     @Nullable List<Event> mEvents;
76 
77     /**
78      * Returns the unique identifier of this FillEventHistory.
79      *
80      * <p>This is used to differentiate individual FillEventHistory.
81      */
82     @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
getSessionId()83     public int getSessionId() {
84         return mSessionId;
85     }
86 
87     /**
88      * Returns the client state set in the previous {@link FillResponse}.
89      *
90      * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
91      * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
92      * , which is not necessary the same app being autofilled now.
93      *
94      * @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead.
95      */
96     @Deprecated
getClientState()97     @Nullable public Bundle getClientState() {
98         return mClientState;
99     }
100 
101     /**
102      * Returns the events occurred after the latest call to
103      * {@link FillCallback#onSuccess(FillResponse)}.
104      *
105      * @return The list of events or {@code null} if non occurred.
106      */
getEvents()107     @Nullable public List<Event> getEvents() {
108         return mEvents;
109     }
110 
111     /**
112      * @hide
113      */
addEvent(Event event)114     public void addEvent(Event event) {
115         if (mEvents == null) {
116             mEvents = new ArrayList<>(1);
117         }
118         mEvents.add(event);
119     }
120 
121     /**
122      * @hide
123      */
FillEventHistory(int sessionId, @Nullable Bundle clientState)124     public FillEventHistory(int sessionId, @Nullable Bundle clientState) {
125         mClientState = clientState;
126         mSessionId = sessionId;
127     }
128 
129     @Override
toString()130     public String toString() {
131         return mEvents == null ? "no events" : mEvents.toString();
132     }
133 
134     @Override
describeContents()135     public int describeContents() {
136         return 0;
137     }
138 
139     @Override
writeToParcel(Parcel parcel, int flags)140     public void writeToParcel(Parcel parcel, int flags) {
141         parcel.writeBundle(mClientState);
142         if (mEvents == null) {
143             parcel.writeInt(0);
144         } else {
145             parcel.writeInt(mEvents.size());
146 
147             int numEvents = mEvents.size();
148             for (int i = 0; i < numEvents; i++) {
149                 Event event = mEvents.get(i);
150                 parcel.writeInt(event.mEventType);
151                 parcel.writeString(event.mDatasetId);
152                 parcel.writeBundle(event.mClientState);
153                 parcel.writeStringList(event.mSelectedDatasetIds);
154                 parcel.writeArraySet(event.mIgnoredDatasetIds);
155                 parcel.writeTypedList(event.mChangedFieldIds);
156                 parcel.writeStringList(event.mChangedDatasetIds);
157 
158                 parcel.writeTypedList(event.mManuallyFilledFieldIds);
159                 if (event.mManuallyFilledFieldIds != null) {
160                     final int size = event.mManuallyFilledFieldIds.size();
161                     for (int j = 0; j < size; j++) {
162                         parcel.writeStringList(event.mManuallyFilledDatasetIds.get(j));
163                     }
164                 }
165                 final AutofillId[] detectedFields = event.mDetectedFieldIds;
166                 parcel.writeParcelableArray(detectedFields, flags);
167                 if (detectedFields != null) {
168                     FieldClassification.writeArrayToParcel(parcel,
169                             event.mDetectedFieldClassifications);
170                 }
171                 parcel.writeInt(event.mSaveDialogNotShowReason);
172                 parcel.writeInt(event.mUiType);
173                 if (Flags.addLastFocusedIdToFillEventHistory()) {
174                     parcel.writeParcelable(event.mFocusedId, 0);
175                 }
176             }
177         }
178     }
179 
180     /**
181      * Description of an event that occurred after the latest call to
182      * {@link FillCallback#onSuccess(FillResponse)}.
183      */
184     public static final class Event {
185         /**
186          * A dataset was selected. The dataset selected can be read from {@link #getDatasetId()}.
187          *
188          * <p><b>Note: </b>on Android {@link android.os.Build.VERSION_CODES#O}, this event was also
189          * incorrectly reported after a
190          * {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
191          * selected and the service returned a dataset in the
192          * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT} of the activity launched from that
193          * {@link IntentSender}. This behavior was fixed on Android
194          * {@link android.os.Build.VERSION_CODES#O_MR1}.
195          */
196         public static final int TYPE_DATASET_SELECTED = 0;
197 
198         /**
199          * A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
200          * selected. The dataset authenticated can be read from {@link #getDatasetId()}.
201          */
202         public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1;
203 
204         /**
205          * A {@link FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[],
206          * IntentSender, android.widget.RemoteViews) fill response authentication} was selected.
207          */
208         public static final int TYPE_AUTHENTICATION_SELECTED = 2;
209 
210         /** A save UI was shown. */
211         public static final int TYPE_SAVE_SHOWN = 3;
212 
213         /**
214          * A committed autofill context for which the autofill service provided datasets.
215          *
216          * <p>This event is useful to track:
217          * <ul>
218          *   <li>Which datasets (if any) were selected by the user
219          *       ({@link #getSelectedDatasetIds()}).
220          *   <li>Which datasets (if any) were NOT selected by the user
221          *       ({@link #getIgnoredDatasetIds()}).
222          *   <li>Which fields in the selected datasets were changed by the user after the dataset
223          *       was selected ({@link #getChangedFields()}.
224          *   <li>Which fields match the {@link UserData} set by the service.
225          * </ul>
226          *
227          * <p><b>Note: </b>This event is only generated when:
228          * <ul>
229          *   <li>The autofill context is committed.
230          *   <li>The service provides at least one dataset in the
231          *       {@link FillResponse fill responses} associated with the context.
232          *   <li>The last {@link FillResponse fill responses} associated with the context has the
233          *       {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} flag.
234          * </ul>
235          *
236          * <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
237          * contexts.
238          */
239         public static final int TYPE_CONTEXT_COMMITTED = 4;
240 
241         /**
242          * A dataset selector was shown.
243          *
244          * <p>This event is fired whenever the autofill UI was presented to the user.</p>
245          */
246         public static final int TYPE_DATASETS_SHOWN = 5;
247 
248         /**
249          * The app/user requested for a field to be Autofilled.
250          *
251          * This event is fired when the view has been entered (by user or app) in order
252          * to differentiate from FillRequests that have been pretriggered for FillDialogs.
253          *
254          * For example, the user might navigate away from a screen without tapping any
255          * fields. In this case, a FillRequest/FillResponse has been generated, but was
256          * not used for Autofilling. The user did not intend to see an Autofill result,
257          * but a FillRequest was still generated. This is different from when the user
258          * did tap on a field after the pretriggered FillRequest, this event will appear
259          * in the FillEventHistory, signaling that the user did intend to Autofill
260          * something.
261          */
262         public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6;
263 
264         /** @hide */
265         @IntDef(prefix = { "TYPE_" }, value = {
266                 TYPE_DATASET_SELECTED,
267                 TYPE_DATASET_AUTHENTICATION_SELECTED,
268                 TYPE_AUTHENTICATION_SELECTED,
269                 TYPE_SAVE_SHOWN,
270                 TYPE_CONTEXT_COMMITTED,
271                 TYPE_DATASETS_SHOWN,
272                 TYPE_VIEW_REQUESTED_AUTOFILL
273         })
274         @Retention(RetentionPolicy.SOURCE)
275         @interface EventIds{}
276 
277         /** No reason for save dialog. */
278         public static final int NO_SAVE_UI_REASON_NONE = 0;
279 
280         /** The SaveInfo associated with the FillResponse is null. */
281         public static final int NO_SAVE_UI_REASON_NO_SAVE_INFO = 1;
282 
283         /** The service asked to delay save. */
284         public static final int NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG = 2;
285 
286         /** There was empty value for required ids. */
287         public static final int NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED = 3;
288 
289         /** No value has been changed. */
290         public static final int NO_SAVE_UI_REASON_NO_VALUE_CHANGED = 4;
291 
292         /** Fields failed validation. */
293         public static final int NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED = 5;
294 
295         /** All fields matched contents of datasets. */
296         public static final int NO_SAVE_UI_REASON_DATASET_MATCH = 6;
297 
298         /**
299          * Credential Manager is invoked instead of Autofill. When that happens, Save Dialog cannot
300          * be shown, and this will be populated in
301          */
302         @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
303         public static final int NO_SAVE_UI_REASON_USING_CREDMAN = 7;
304 
305         /** @hide */
306         @IntDef(prefix = { "NO_SAVE_UI_REASON_" }, value = {
307                 NO_SAVE_UI_REASON_NONE,
308                 NO_SAVE_UI_REASON_NO_SAVE_INFO,
309                 NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG,
310                 NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED,
311                 NO_SAVE_UI_REASON_NO_VALUE_CHANGED,
312                 NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED,
313                 NO_SAVE_UI_REASON_DATASET_MATCH
314         })
315         @Retention(RetentionPolicy.SOURCE)
316         public @interface NoSaveReason{}
317 
318         /** The autofill suggestion presentation is unknown, this will be set for the event
319          * that is unrelated to fill Ui presentation */
320         public static final int UI_TYPE_UNKNOWN = 0;
321 
322         /** The autofill suggestion is shown as a menu popup presentation. */
323         public static final int UI_TYPE_MENU = 1;
324 
325         /** The autofill suggestion is shown as a keyboard inline presentation. */
326         public static final int UI_TYPE_INLINE = 2;
327 
328         /** The autofill suggestion is shown as a dialog presentation. */
329         public static final int UI_TYPE_DIALOG = 3;
330 
331         /**
332          * The autofill suggestion is shown os a credman bottom sheet
333          *
334          * <p>Note, this was introduced as bottom sheet even though it applies to all credman UI
335          * types. Instead of exposing this directly to the public, the generic UI_TYPE_CREDMAN is
336          * introduced with the same number.
337          *
338          * @hide
339          */
340         public static final int UI_TYPE_CREDMAN_BOTTOM_SHEET = 4;
341 
342         /** Credential Manager suggestions are shown instead of Autofill suggestion */
343         @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
344         public static final int UI_TYPE_CREDENTIAL_MANAGER = 4;
345 
346         /** @hide */
347         @IntDef(prefix = { "UI_TYPE_" }, value = {
348                 UI_TYPE_UNKNOWN,
349                 UI_TYPE_MENU,
350                 UI_TYPE_INLINE,
351                 UI_TYPE_DIALOG,
352                 UI_TYPE_CREDMAN_BOTTOM_SHEET
353         })
354         @Retention(RetentionPolicy.SOURCE)
355         public @interface UiType {}
356 
357         @EventIds private final int mEventType;
358         @Nullable private final String mDatasetId;
359         @Nullable private final Bundle mClientState;
360 
361         // Note: mSelectedDatasetIds is stored as List<> instead of Set because Session already
362         // stores it as List
363         @Nullable private final List<String> mSelectedDatasetIds;
364         @Nullable private final ArraySet<String> mIgnoredDatasetIds;
365 
366         @Nullable private final ArrayList<AutofillId> mChangedFieldIds;
367         @Nullable private final ArrayList<String> mChangedDatasetIds;
368 
369         @Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
370         @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
371 
372         @Nullable private final AutofillId[] mDetectedFieldIds;
373         @Nullable private final FieldClassification[] mDetectedFieldClassifications;
374 
375         @NoSaveReason private final int mSaveDialogNotShowReason;
376 
377 
378         @UiType
379         private final int mUiType;
380 
381         @Nullable private final AutofillId mFocusedId;
382 
383         /**
384          * Returns the type of the event.
385          *
386          * @return The type of the event
387          */
getType()388         public int getType() {
389             return mEventType;
390         }
391 
392         /** Gets the {@code AutofillId} that's focused at the time of action */
393         @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
394         @Nullable
getFocusedId()395         public AutofillId getFocusedId() {
396             return mFocusedId;
397         }
398 
399         /**
400          * Returns the id of dataset the id was on.
401          *
402          * @return The id of dataset, or {@code null} the event is not associated with a dataset.
403          */
getDatasetId()404         @Nullable public String getDatasetId() {
405             return mDatasetId;
406         }
407 
408         /**
409          * Returns the client state from the {@link FillResponse} used to generate this event.
410          *
411          * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
412          * {@link
413          * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)},
414          * which is not necessary the same app being autofilled now.
415          */
getClientState()416         @Nullable public Bundle getClientState() {
417             return mClientState;
418         }
419 
420         /**
421          * Returns which datasets were selected by the user.
422          *
423          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
424          */
getSelectedDatasetIds()425         @NonNull public Set<String> getSelectedDatasetIds() {
426             return mSelectedDatasetIds == null ? Collections.emptySet()
427                     : new ArraySet<>(mSelectedDatasetIds);
428         }
429 
430         /**
431          * Returns which datasets were shown to the user.
432          *
433          * <p><b>Note: </b>Only set on events of type {@link #TYPE_DATASETS_SHOWN}.
434          */
435         @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
436         @NonNull
getShownDatasetIds()437         public Set<String> getShownDatasetIds() {
438             return Collections.emptySet();
439         }
440 
441         /**
442          * Returns which datasets were NOT selected by the user.
443          *
444          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
445          */
getIgnoredDatasetIds()446         @NonNull public Set<String> getIgnoredDatasetIds() {
447             return mIgnoredDatasetIds == null ? Collections.emptySet() : mIgnoredDatasetIds;
448         }
449 
450         /**
451          * Returns which fields in the selected datasets were changed by the user after the dataset
452          * was selected.
453          *
454          * <p>For example, server provides:
455          *
456          * <pre class="prettyprint">
457          *  FillResponse response = new FillResponse.Builder()
458          *      .addDataset(new Dataset.Builder(presentation1)
459          *          .setId("4815")
460          *          .setValue(usernameId, AutofillValue.forText("MrPlow"))
461          *          .build())
462          *      .addDataset(new Dataset.Builder(presentation2)
463          *          .setId("162342")
464          *          .setValue(passwordId, AutofillValue.forText("D'OH"))
465          *          .build())
466          *      .build();
467          * </pre>
468          *
469          * <p>User select both datasets (for username and password) but after the fields are
470          * autofilled, user changes them to:
471          *
472          * <pre class="prettyprint">
473          *   username = "ElBarto";
474          *   password = "AyCaramba";
475          * </pre>
476          *
477          * <p>Then the result is the following map:
478          *
479          * <pre class="prettyprint">
480          *   usernameId => "4815"
481          *   passwordId => "162342"
482          * </pre>
483          *
484          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
485          *
486          * @return map map whose key is the id of the change fields, and value is the id of
487          * dataset that has that field and was selected by the user.
488          */
getChangedFields()489         @NonNull public Map<AutofillId, String> getChangedFields() {
490             if (mChangedFieldIds == null || mChangedDatasetIds == null) {
491                 return Collections.emptyMap();
492             }
493 
494             final int size = mChangedFieldIds.size();
495             final ArrayMap<AutofillId, String> changedFields = new ArrayMap<>(size);
496             for (int i = 0; i < size; i++) {
497                 changedFields.put(mChangedFieldIds.get(i), mChangedDatasetIds.get(i));
498             }
499             return changedFields;
500         }
501 
502         /**
503          * Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
504          * results.
505          *
506          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
507          * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
508          * field classification}.
509          */
getFieldsClassification()510         @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
511             if (mDetectedFieldIds == null) {
512                 return Collections.emptyMap();
513             }
514             final int size = mDetectedFieldIds.length;
515             final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
516             for (int i = 0; i < size; i++) {
517                 final AutofillId id = mDetectedFieldIds[i];
518                 final FieldClassification fc = mDetectedFieldClassifications[i];
519                 if (sVerbose) {
520                     Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", fc=" + fc);
521                 }
522                 map.put(id, fc);
523             }
524             return map;
525         }
526 
527         /**
528          * Returns which fields were available on datasets provided by the service but manually
529          * entered by the user.
530          *
531          * <p>For example, server provides:
532          *
533          * <pre class="prettyprint">
534          *  FillResponse response = new FillResponse.Builder()
535          *      .addDataset(new Dataset.Builder(presentation1)
536          *          .setId("4815")
537          *          .setValue(usernameId, AutofillValue.forText("MrPlow"))
538          *          .setValue(passwordId, AutofillValue.forText("AyCaramba"))
539          *          .build())
540          *      .addDataset(new Dataset.Builder(presentation2)
541          *          .setId("162342")
542          *          .setValue(usernameId, AutofillValue.forText("ElBarto"))
543          *          .setValue(passwordId, AutofillValue.forText("D'OH"))
544          *          .build())
545          *      .addDataset(new Dataset.Builder(presentation3)
546          *          .setId("108")
547          *          .setValue(usernameId, AutofillValue.forText("MrPlow"))
548          *          .setValue(passwordId, AutofillValue.forText("D'OH"))
549          *          .build())
550          *      .build();
551          * </pre>
552          *
553          * <p>User doesn't select a dataset but manually enters:
554          *
555          * <pre class="prettyprint">
556          *   username = "MrPlow";
557          *   password = "D'OH";
558          * </pre>
559          *
560          * <p>Then the result is the following map:
561          *
562          * <pre class="prettyprint">
563          *   usernameId => { "4815", "108"}
564          *   passwordId => { "162342", "108" }
565          * </pre>
566          *
567          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
568          *
569          * @return map map whose key is the id of the manually-entered field, and value is the
570          * ids of the datasets that have that value but were not selected by the user.
571          */
getManuallyEnteredField()572         @NonNull public Map<AutofillId, Set<String>> getManuallyEnteredField() {
573             if (mManuallyFilledFieldIds == null || mManuallyFilledDatasetIds == null) {
574                 return Collections.emptyMap();
575             }
576 
577             final int size = mManuallyFilledFieldIds.size();
578             final Map<AutofillId, Set<String>> manuallyFilledFields = new ArrayMap<>(size);
579             for (int i = 0; i < size; i++) {
580                 final AutofillId fieldId = mManuallyFilledFieldIds.get(i);
581                 final ArrayList<String> datasetIds = mManuallyFilledDatasetIds.get(i);
582                 manuallyFilledFields.put(fieldId, new ArraySet<>(datasetIds));
583             }
584             return manuallyFilledFields;
585         }
586 
587         /**
588          * Returns the reason why a save dialog was not shown.
589          *
590          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other
591          * event types, the reason is set to NO_SAVE_UI_REASON_NONE.
592          *
593          * @return The reason why a save dialog was not shown.
594          */
595         @NoSaveReason
getNoSaveUiReason()596         public int getNoSaveUiReason() {
597             return mSaveDialogNotShowReason;
598         }
599 
600         /**
601          * Returns fill suggestion ui presentation type which corresponds to types
602          * defined in {@link android.service.autofill.Presentations).
603          *
604          * <p><b>Note: </b>Only set on events of type {@link #TYPE_DATASETS_SHOWN} and
605          * {@link #TYPE_DATASET_SELECTED}. For the other event types, the type is set to
606          * {@link #UI_TYPE_UNKNOWN }.
607          *
608          * @return The ui presentation type shown for user.
609          */
610         @UiType
getUiType()611         public int getUiType() {
612             return mUiType;
613         }
614 
615         /**
616          * Creates a new event.
617          *
618          * @param eventType The type of the event
619          * @param datasetId The dataset the event was on, or {@code null} if the event was on the
620          *                  whole response.
621          * @param clientState The client state associated with the event.
622          * @param selectedDatasetIds The ids of datasets selected by the user.
623          * @param ignoredDatasetIds The ids of datasets NOT select by the user.
624          * @param changedFieldIds The ids of fields changed by the user.
625          * @param changedDatasetIds The ids of the datasets that havd values matching the
626          * respective entry on {@code changedFieldIds}.
627          * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
628          * and belonged to datasets.
629          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
630          * respective entry on {@code manuallyFilledFieldIds}.
631          * @param detectedFieldClassifications the field classification matches.
632          * @param focusedId the field which was focused at the time of event trigger
633          *
634          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
635          * {@code changedDatasetIds} doesn't match.
636          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
637          * {@code manuallyFilledDatasetIds} doesn't match.
638          *
639          * @hide
640          */
Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, @Nullable List<String> selectedDatasetIds, @Nullable ArraySet<String> ignoredDatasetIds, @Nullable ArrayList<AutofillId> changedFieldIds, @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, @Nullable AutofillId focusedId)641         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
642                 @Nullable List<String> selectedDatasetIds,
643                 @Nullable ArraySet<String> ignoredDatasetIds,
644                 @Nullable ArrayList<AutofillId> changedFieldIds,
645                 @Nullable ArrayList<String> changedDatasetIds,
646                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
647                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
648                 @Nullable AutofillId[] detectedFieldIds,
649                 @Nullable FieldClassification[] detectedFieldClassifications,
650                 @Nullable AutofillId focusedId) {
651             this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
652                     changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
653                     manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
654                     NO_SAVE_UI_REASON_NONE, focusedId);
655         }
656 
657         /**
658          * Creates a new event.
659          *
660          * @param eventType The type of the event
661          * @param datasetId The dataset the event was on, or {@code null} if the event was on the
662          *                  whole response.
663          * @param clientState The client state associated with the event.
664          * @param selectedDatasetIds The ids of datasets selected by the user.
665          * @param ignoredDatasetIds The ids of datasets NOT select by the user.
666          * @param changedFieldIds The ids of fields changed by the user.
667          * @param changedDatasetIds The ids of the datasets that havd values matching the
668          * respective entry on {@code changedFieldIds}.
669          * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
670          * and belonged to datasets.
671          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
672          * respective entry on {@code manuallyFilledFieldIds}.
673          * @param detectedFieldClassifications the field classification matches.
674          * @param saveDialogNotShowReason The reason why a save dialog was not shown.
675          * @param focusedId the field which was focused at the time of event trigger
676          *
677          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
678          * {@code changedDatasetIds} doesn't match.
679          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
680          * {@code manuallyFilledDatasetIds} doesn't match.
681          *
682          * @hide
683          */
Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, @Nullable List<String> selectedDatasetIds, @Nullable ArraySet<String> ignoredDatasetIds, @Nullable ArrayList<AutofillId> changedFieldIds, @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, int saveDialogNotShowReason, @Nullable AutofillId focusedId)684         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
685                 @Nullable List<String> selectedDatasetIds,
686                 @Nullable ArraySet<String> ignoredDatasetIds,
687                 @Nullable ArrayList<AutofillId> changedFieldIds,
688                 @Nullable ArrayList<String> changedDatasetIds,
689                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
690                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
691                 @Nullable AutofillId[] detectedFieldIds,
692                 @Nullable FieldClassification[] detectedFieldClassifications,
693                 int saveDialogNotShowReason,
694                 @Nullable AutofillId focusedId) {
695             this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
696                     changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
697                     manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
698                     saveDialogNotShowReason, UI_TYPE_UNKNOWN, focusedId);
699         }
700 
701         /**
702          * Creates a new event.
703          *
704          * @param eventType The type of the event
705          * @param datasetId The dataset the event was on, or {@code null} if the event was on the
706          *                  whole response.
707          * @param clientState The client state associated with the event.
708          * @param selectedDatasetIds The ids of datasets selected by the user.
709          * @param ignoredDatasetIds The ids of datasets NOT select by the user.
710          * @param changedFieldIds The ids of fields changed by the user.
711          * @param changedDatasetIds The ids of the datasets that havd values matching the
712          * respective entry on {@code changedFieldIds}.
713          * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
714          * and belonged to datasets.
715          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
716          * respective entry on {@code manuallyFilledFieldIds}.
717          * @param detectedFieldClassifications the field classification matches.
718          * @param saveDialogNotShowReason The reason why a save dialog was not shown.
719          * @param uiType The ui presentation type for fill suggestion.
720          * @param focusedId the field which was focused at the time of event trigger
721          *
722          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
723          * {@code changedDatasetIds} doesn't match.
724          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
725          * {@code manuallyFilledDatasetIds} doesn't match.
726          *
727          * @hide
728          */
Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, @Nullable List<String> selectedDatasetIds, @Nullable ArraySet<String> ignoredDatasetIds, @Nullable ArrayList<AutofillId> changedFieldIds, @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, int saveDialogNotShowReason, int uiType, @Nullable AutofillId focusedId)729         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
730                 @Nullable List<String> selectedDatasetIds,
731                 @Nullable ArraySet<String> ignoredDatasetIds,
732                 @Nullable ArrayList<AutofillId> changedFieldIds,
733                 @Nullable ArrayList<String> changedDatasetIds,
734                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
735                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
736                 @Nullable AutofillId[] detectedFieldIds,
737                 @Nullable FieldClassification[] detectedFieldClassifications,
738                 int saveDialogNotShowReason, int uiType, @Nullable AutofillId focusedId) {
739             mEventType = Preconditions.checkArgumentInRange(eventType, 0,
740                     TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
741             mDatasetId = datasetId;
742             mClientState = clientState;
743             mSelectedDatasetIds = selectedDatasetIds;
744             mIgnoredDatasetIds = ignoredDatasetIds;
745             if (changedFieldIds != null) {
746                 Preconditions.checkArgument(!ArrayUtils.isEmpty(changedFieldIds)
747                                 && changedDatasetIds != null
748                                 && changedFieldIds.size() == changedDatasetIds.size(),
749                         "changed ids must have same length and not be empty");
750             }
751             mChangedFieldIds = changedFieldIds;
752             mChangedDatasetIds = changedDatasetIds;
753             if (manuallyFilledFieldIds != null) {
754                 Preconditions.checkArgument(!ArrayUtils.isEmpty(manuallyFilledFieldIds)
755                                 && manuallyFilledDatasetIds != null
756                                 && manuallyFilledFieldIds.size() == manuallyFilledDatasetIds.size(),
757                         "manually filled ids must have same length and not be empty");
758             }
759             mManuallyFilledFieldIds = manuallyFilledFieldIds;
760             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
761 
762             mDetectedFieldIds = detectedFieldIds;
763             mDetectedFieldClassifications = detectedFieldClassifications;
764 
765             mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason,
766                     NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH,
767                     "saveDialogNotShowReason");
768             mUiType = uiType;
769             mFocusedId = focusedId;
770         }
771 
772         @Override
toString()773         public String toString() {
774             return "FillEvent [datasetId=" + mDatasetId
775                     + ", type=" + eventToString(mEventType)
776                     + ", uiType=" + uiTypeToString(mUiType)
777                     + ", selectedDatasets=" + mSelectedDatasetIds
778                     + ", ignoredDatasetIds=" + mIgnoredDatasetIds
779                     + ", changedFieldIds=" + mChangedFieldIds
780                     + ", changedDatasetsIds=" + mChangedDatasetIds
781                     + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
782                     + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
783                     + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
784                     + ", detectedFieldClassifications ="
785                         + Arrays.toString(mDetectedFieldClassifications)
786                     + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason
787                     + "]";
788         }
789 
eventToString(int eventType)790         private static String eventToString(int eventType) {
791             switch (eventType) {
792                 case TYPE_DATASET_SELECTED:
793                     return "TYPE_DATASET_SELECTED";
794                 case TYPE_DATASET_AUTHENTICATION_SELECTED:
795                     return "TYPE_DATASET_AUTHENTICATION_SELECTED";
796                 case TYPE_AUTHENTICATION_SELECTED:
797                     return "TYPE_AUTHENTICATION_SELECTED";
798                 case TYPE_SAVE_SHOWN:
799                     return "TYPE_SAVE_SHOWN";
800                 case TYPE_CONTEXT_COMMITTED:
801                     return "TYPE_CONTEXT_COMMITTED";
802                 case TYPE_DATASETS_SHOWN:
803                     return "TYPE_DATASETS_SHOWN";
804                 case TYPE_VIEW_REQUESTED_AUTOFILL:
805                     return "TYPE_VIEW_REQUESTED_AUTOFILL";
806                 default:
807                     return "TYPE_UNKNOWN";
808             }
809         }
810 
uiTypeToString(int uiType)811         private static String uiTypeToString(int uiType) {
812             switch (uiType) {
813                 case UI_TYPE_MENU:
814                     return "UI_TYPE_MENU";
815                 case UI_TYPE_INLINE:
816                     return "UI_TYPE_INLINE";
817                 case UI_TYPE_DIALOG:
818                     return "UI_TYPE_FILL_DIALOG";
819                 case UI_TYPE_CREDMAN_BOTTOM_SHEET:
820                     return "UI_TYPE_CREDMAN_BOTTOM_SHEET";
821                 default:
822                     return "UI_TYPE_UNKNOWN";
823             }
824         }
825     }
826 
827     public static final @android.annotation.NonNull Parcelable.Creator<FillEventHistory> CREATOR =
828             new Parcelable.Creator<FillEventHistory>() {
829                 @Override
830                 public FillEventHistory createFromParcel(Parcel parcel) {
831                     FillEventHistory selection = new FillEventHistory(0, parcel.readBundle());
832 
833                     final int numEvents = parcel.readInt();
834                     for (int i = 0; i < numEvents; i++) {
835                         final int eventType = parcel.readInt();
836                         final String datasetId = parcel.readString();
837                         final Bundle clientState = parcel.readBundle();
838                         final ArrayList<String> selectedDatasetIds = parcel.createStringArrayList();
839                         @SuppressWarnings("unchecked")
840                         final ArraySet<String> ignoredDatasets =
841                                 (ArraySet<String>) parcel.readArraySet(null);
842                         final ArrayList<AutofillId> changedFieldIds =
843                                 parcel.createTypedArrayList(AutofillId.CREATOR);
844                         final ArrayList<String> changedDatasetIds = parcel.createStringArrayList();
845 
846                         final ArrayList<AutofillId> manuallyFilledFieldIds =
847                                 parcel.createTypedArrayList(AutofillId.CREATOR);
848                         final ArrayList<ArrayList<String>> manuallyFilledDatasetIds;
849                         if (manuallyFilledFieldIds != null) {
850                             final int size = manuallyFilledFieldIds.size();
851                             manuallyFilledDatasetIds = new ArrayList<>(size);
852                             for (int j = 0; j < size; j++) {
853                                 manuallyFilledDatasetIds.add(parcel.createStringArrayList());
854                             }
855                         } else {
856                             manuallyFilledDatasetIds = null;
857                         }
858                         final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
859                                 AutofillId.class);
860                         final FieldClassification[] detectedFieldClassifications =
861                                 (detectedFieldIds != null)
862                                 ? FieldClassification.readArrayFromParcel(parcel)
863                                 : null;
864                         final int saveDialogNotShowReason = parcel.readInt();
865                         final int uiType = parcel.readInt();
866                         AutofillId focusedId = null;
867                         if (Flags.addLastFocusedIdToFillEventHistory()) {
868                             focusedId = parcel.readParcelable(null, AutofillId.class);
869                         }
870 
871                         selection.addEvent(new Event(eventType, datasetId, clientState,
872                                 selectedDatasetIds, ignoredDatasets,
873                                 changedFieldIds, changedDatasetIds,
874                                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
875                                 detectedFieldIds, detectedFieldClassifications,
876                                 saveDialogNotShowReason, uiType, focusedId));
877                     }
878                     return selection;
879                 }
880 
881                 @Override
882                 public FillEventHistory[] newArray(int size) {
883                     return new FillEventHistory[size];
884                 }
885             };
886 }
887