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