• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.sDebug;
20 
21 import android.annotation.Hide;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SuppressLint;
26 import android.annotation.SystemApi;
27 import android.annotation.TestApi;
28 import android.content.ClipData;
29 import android.content.Intent;
30 import android.content.IntentSender;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.util.ArrayMap;
34 import android.view.autofill.AutofillId;
35 import android.view.autofill.AutofillManager;
36 import android.view.autofill.AutofillValue;
37 import android.widget.RemoteViews;
38 
39 import com.android.internal.util.Preconditions;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.util.ArrayList;
44 import java.util.Objects;
45 import java.util.regex.Pattern;
46 
47 /**
48  * <p>A <code>Dataset</code> object represents a group of fields (key / value pairs) used
49  * to autofill parts of a screen.
50  *
51  * <p>For more information about the role of datasets in the autofill workflow, read
52  * <a href="/guide/topics/text/autofill-services">Build autofill services</a> and the
53  * <code><a href="/reference/android/service/autofill/AutofillService">AutofillService</a></code>
54  * documentation.
55  *
56  * <a name="BasicUsage"></a>
57  * <h3>Basic usage</h3>
58  *
59  * <p>In its simplest form, a dataset contains one or more fields (comprised of
60  * an {@link AutofillId id}, a {@link AutofillValue value}, and an optional filter
61  * {@link Pattern regex}); and one or more {@link RemoteViews presentations} for these fields
62  * (each field could have its own {@link RemoteViews presentation}, or use the default
63  * {@link RemoteViews presentation} associated with the whole dataset).
64  *
65  * <p>When an autofill service returns datasets in a {@link FillResponse}
66  * and the screen input is focused in a view that is present in at least one of these datasets,
67  * the Android System displays a UI containing the {@link RemoteViews presentation} of
68  * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
69  * dataset from the UI, all views in that dataset are autofilled.
70  *
71  * <p>If both the current Input Method and autofill service supports inline suggestions, the Dataset
72  * can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain
73  * an {@link InlinePresentation} representing how the inline suggestion UI will be rendered.
74  *
75  * <a name="FillDialogUI"></a>
76  * <h3>Fill Dialog UI</h3>
77  *
78  * <p>The fill dialog UI is a more conspicuous and efficient interface than dropdown UI. If autofill
79  * suggestions are available when the user clicks on a field that supports filling the dialog UI,
80  * Autofill will pop up a fill dialog. The dialog will take up a larger area to display the
81  * datasets, so it is easy for users to pay attention to the datasets and selecting a dataset.
82  * If the user focuses on the view before suggestions are available, will fall back to dropdown UI
83  * or inline suggestions.
84  *
85  * <a name="Authentication"></a>
86  * <h3>Dataset authentication</h3>
87  *
88  * <p>In a more sophisticated form, the dataset values can be protected until the user authenticates
89  * the dataset&mdash;in that case, when a dataset is selected by the user, the Android System
90  * launches an intent set by the service to "unlock" the dataset.
91  *
92  * <p>For example, when a data set contains credit card information (such as number,
93  * expiration date, and verification code), you could provide a dataset presentation saying
94  * "Tap to authenticate". Then when the user taps that option, you would launch an activity asking
95  * the user to enter the credit card code, and if the user enters a valid code, you could then
96  * "unlock" the dataset.
97  *
98  * <p>You can also use authenticated datasets to offer an interactive UI for the user. For example,
99  * if the activity being autofilled is an account creation screen, you could use an authenticated
100  * dataset to automatically generate a random password for the user.
101  *
102  * <p>See {@link Dataset.Builder#setAuthentication(IntentSender)} for more details about the dataset
103  * authentication mechanism.
104  *
105  * <a name="Filtering"></a>
106  * <h3>Filtering</h3>
107  * <p>The autofill UI automatically changes which values are shown based on value of the view
108  * anchoring it, following the rules below:
109  * <ol>
110  *   <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not
111  * {@link AutofillValue#isText() text} or is empty, all datasets are shown.
112  *   <li>Datasets that have a filter regex (set through {@link Field.Builder#setFilter(Pattern)}
113  *   and {@link Dataset.Builder#setField(AutofillId, Field)}) and whose regex matches the view's
114  *   text value converted to lower case are shown.
115  *   <li>Datasets that do not require authentication, have a field value that is
116  * {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts
117  * with the lower case value of the view's text are shown.
118  *   <li>All other datasets are hidden.
119  * </ol>
120  * <p>Note: If user enters four or more characters, all datasets will be hidden</p>
121  *
122  */
123 public final class Dataset implements Parcelable {
124     /**
125      * This dataset is picked because of unknown reason.
126      * @hide
127      */
128     public static final int PICK_REASON_UNKNOWN = 0;
129     /**
130      * This dataset is picked because pcc wasn't enabled.
131      * @hide
132      */
133     public static final int PICK_REASON_NO_PCC = 1;
134     /**
135      * This dataset is picked because provider gave this dataset.
136      * @hide
137      */
138     public static final int PICK_REASON_PROVIDER_DETECTION_ONLY = 2;
139     /**
140      * This dataset is picked because provider detection was preferred. However, provider also made
141      * this dataset available for PCC detected types, so they could've been picked up by PCC
142      * detection. This however doesn't imply that this dataset would've been chosen for sure. For
143      * eg, if PCC Detection was preferred, and PCC detected other field types, which wasn't
144      * applicable to this dataset, it wouldn't have been shown.
145      * @hide
146      */
147     public static final int PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC = 3;
148     /**
149      * This dataset is picked because of PCC detection was chosen.
150      * @hide
151      */
152     public static final int PICK_REASON_PCC_DETECTION_ONLY = 4;
153     /**
154      * This dataset is picked because of PCC Detection was preferred. However, Provider also gave
155      * this dataset, so if PCC wasn't enabled, this dataset would've been eligible anyway.
156      * @hide
157      */
158     public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER = 5;
159 
160     /**
161      * Reason why the dataset was eligible for autofill.
162      * @hide
163      */
164     @IntDef(prefix = { "PICK_REASON_" }, value = {
165             PICK_REASON_UNKNOWN,
166             PICK_REASON_NO_PCC,
167             PICK_REASON_PROVIDER_DETECTION_ONLY,
168             PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC,
169             PICK_REASON_PCC_DETECTION_ONLY,
170             PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER,
171     })
172     @Retention(RetentionPolicy.SOURCE)
173     public @interface DatasetEligibleReason{}
174 
175     private @DatasetEligibleReason int mEligibleReason;
176 
177     private final ArrayList<AutofillId> mFieldIds;
178     private final ArrayList<AutofillValue> mFieldValues;
179     private final ArrayList<RemoteViews> mFieldPresentations;
180     private final ArrayList<RemoteViews> mFieldDialogPresentations;
181     private final ArrayList<InlinePresentation> mFieldInlinePresentations;
182     private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
183     private final ArrayList<DatasetFieldFilter> mFieldFilters;
184     private final ArrayList<String> mAutofillDatatypes;
185 
186     @Nullable private final ClipData mFieldContent;
187     private final RemoteViews mPresentation;
188     private final RemoteViews mDialogPresentation;
189     @Nullable private final InlinePresentation mInlinePresentation;
190     @Nullable private final InlinePresentation mInlineTooltipPresentation;
191     private final IntentSender mAuthentication;
192 
193     @Nullable private Intent mCredentialFillInIntent;
194 
195     @Nullable String mId;
196 
197     /**
198      * Constructor to copy the dataset, but replaces the AutofillId with the given input.
199      * Useful to modify the field type, and provide autofillId.
200      * @hide
201      */
Dataset( ArrayList<AutofillId> fieldIds, ArrayList<AutofillValue> fieldValues, ArrayList<RemoteViews> fieldPresentations, ArrayList<RemoteViews> fieldDialogPresentations, ArrayList<InlinePresentation> fieldInlinePresentations, ArrayList<InlinePresentation> fieldInlineTooltipPresentations, ArrayList<DatasetFieldFilter> fieldFilters, ArrayList<String> autofillDatatypes, ClipData fieldContent, RemoteViews presentation, RemoteViews dialogPresentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation, @Nullable String id, IntentSender authentication)202     public Dataset(
203             ArrayList<AutofillId> fieldIds,
204             ArrayList<AutofillValue> fieldValues,
205             ArrayList<RemoteViews> fieldPresentations,
206             ArrayList<RemoteViews> fieldDialogPresentations,
207             ArrayList<InlinePresentation> fieldInlinePresentations,
208             ArrayList<InlinePresentation> fieldInlineTooltipPresentations,
209             ArrayList<DatasetFieldFilter> fieldFilters,
210             ArrayList<String> autofillDatatypes,
211             ClipData fieldContent,
212             RemoteViews presentation,
213             RemoteViews dialogPresentation,
214             @Nullable InlinePresentation inlinePresentation,
215             @Nullable  InlinePresentation inlineTooltipPresentation,
216             @Nullable String id,
217             IntentSender authentication) {
218         mFieldIds = fieldIds;
219         mFieldValues = fieldValues;
220         mFieldPresentations = fieldPresentations;
221         mFieldDialogPresentations = fieldDialogPresentations;
222         mFieldInlinePresentations = fieldInlinePresentations;
223         mFieldInlineTooltipPresentations = fieldInlineTooltipPresentations;
224         mAutofillDatatypes = autofillDatatypes;
225         mFieldFilters = fieldFilters;
226         mFieldContent = fieldContent;
227         mPresentation = presentation;
228         mDialogPresentation = dialogPresentation;
229         mInlinePresentation = inlinePresentation;
230         mInlineTooltipPresentation = inlineTooltipPresentation;
231         mAuthentication = authentication;
232         mCredentialFillInIntent = null;
233         mId = id;
234     }
235 
236     /**
237      * Constructor to copy the dataset, but replaces the AutofillId with the given input.
238      * Useful to modify the field type, and provide autofillId.
239      * @hide
240      */
Dataset(Dataset dataset, ArrayList<AutofillId> ids)241     public Dataset(Dataset dataset, ArrayList<AutofillId> ids) {
242         mFieldIds = ids;
243         mFieldValues = dataset.mFieldValues;
244         mFieldPresentations = dataset.mFieldPresentations;
245         mFieldDialogPresentations = dataset.mFieldDialogPresentations;
246         mFieldInlinePresentations = dataset.mFieldInlinePresentations;
247         mFieldInlineTooltipPresentations = dataset.mFieldInlineTooltipPresentations;
248         mFieldFilters = dataset.mFieldFilters;
249         mFieldContent = dataset.mFieldContent;
250         mPresentation = dataset.mPresentation;
251         mDialogPresentation = dataset.mDialogPresentation;
252         mInlinePresentation = dataset.mInlinePresentation;
253         mInlineTooltipPresentation = dataset.mInlineTooltipPresentation;
254         mAuthentication = dataset.mAuthentication;
255         mCredentialFillInIntent = dataset.mCredentialFillInIntent;
256         mId = dataset.mId;
257         mAutofillDatatypes = dataset.mAutofillDatatypes;
258     }
259 
Dataset(Builder builder)260     private Dataset(Builder builder) {
261         mFieldIds = builder.mFieldIds;
262         mFieldValues = builder.mFieldValues;
263         mFieldPresentations = builder.mFieldPresentations;
264         mFieldDialogPresentations = builder.mFieldDialogPresentations;
265         mFieldInlinePresentations = builder.mFieldInlinePresentations;
266         mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations;
267         mFieldFilters = builder.mFieldFilters;
268         mFieldContent = builder.mFieldContent;
269         mPresentation = builder.mPresentation;
270         mDialogPresentation = builder.mDialogPresentation;
271         mInlinePresentation = builder.mInlinePresentation;
272         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
273         mAuthentication = builder.mAuthentication;
274         mCredentialFillInIntent = builder.mCredentialFillInIntent;
275         mId = builder.mId;
276         mAutofillDatatypes = builder.mAutofillDatatypes;
277     }
278 
279     /** @hide */
280     @TestApi
281     @SuppressLint({"ConcreteCollection", "NullableCollection"})
getAutofillDatatypes()282     public @Nullable ArrayList<String> getAutofillDatatypes() {
283         return mAutofillDatatypes;
284     }
285 
286     /** @hide */
287     @TestApi
288     @SuppressLint({"ConcreteCollection", "NullableCollection"})
getFieldIds()289     public @Nullable ArrayList<AutofillId> getFieldIds() {
290         return mFieldIds;
291     }
292 
293     /** @hide */
294     @TestApi
295     @SuppressLint({"ConcreteCollection", "NullableCollection"})
getFieldValues()296     public @Nullable ArrayList<AutofillValue> getFieldValues() {
297         return mFieldValues;
298     }
299 
300     /** @hide */
301     @TestApi
getFieldPresentation(int index)302     public @Nullable RemoteViews getFieldPresentation(int index) {
303         final RemoteViews customPresentation = mFieldPresentations.get(index);
304         return customPresentation != null ? customPresentation : mPresentation;
305     }
306 
307     /** @hide */
308     @TestApi
getFieldDialogPresentation(int index)309     public @Nullable RemoteViews getFieldDialogPresentation(int index) {
310         final RemoteViews customPresentation = mFieldDialogPresentations.get(index);
311         return customPresentation != null ? customPresentation : mDialogPresentation;
312     }
313 
314     /** @hide */
315     @TestApi
getFieldInlinePresentation(int index)316     public @Nullable InlinePresentation getFieldInlinePresentation(int index) {
317         final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
318         return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
319     }
320 
321     /** @hide */
322     @TestApi
getFieldInlineTooltipPresentation(int index)323     public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) {
324         final InlinePresentation inlineTooltipPresentation =
325                 mFieldInlineTooltipPresentations.get(index);
326         return inlineTooltipPresentation != null
327                 ? inlineTooltipPresentation : mInlineTooltipPresentation;
328     }
329 
330     /** @hide */
331     @TestApi
getFilter(int index)332     public @Nullable DatasetFieldFilter getFilter(int index) {
333         return mFieldFilters.get(index);
334     }
335 
336     /**
337      * Returns the content to be filled for a non-text suggestion. This is only applicable to
338      * augmented autofill. The target field for the content is available via {@link #getFieldIds()}
339      * (guaranteed to have a single field id set when the return value here is non-null). See
340      * {@link Builder#setContent(AutofillId, ClipData)} for more info.
341      *
342      * @hide
343      */
344     @TestApi
getFieldContent()345     public @Nullable ClipData getFieldContent() {
346         return mFieldContent;
347     }
348 
349     /** @hide */
350     @TestApi
getAuthentication()351     public @Nullable IntentSender getAuthentication() {
352         return mAuthentication;
353     }
354 
355     /** @hide */
356     @Hide
getCredentialFillInIntent()357     public @Nullable Intent getCredentialFillInIntent() {
358         return mCredentialFillInIntent;
359     }
360 
361     /** @hide */
362     @Hide
setCredentialFillInIntent(Intent intent)363     public void setCredentialFillInIntent(Intent intent) {
364         mCredentialFillInIntent = intent;
365     }
366 
367     /** @hide */
368     @TestApi
isEmpty()369     public boolean isEmpty() {
370         return mFieldIds == null || mFieldIds.isEmpty();
371     }
372 
373     @Override
toString()374     public String toString() {
375         if (!sDebug) return super.toString();
376 
377         final StringBuilder builder = new StringBuilder("Dataset[");
378         if (mId == null) {
379             builder.append("noId");
380         } else {
381             // Cannot disclose id because it could contain PII.
382             builder.append("id=").append(mId.length()).append("_chars");
383         }
384         if (mFieldIds != null) {
385             builder.append(", fieldIds=").append(mFieldIds);
386         }
387         if (mFieldValues != null) {
388             builder.append(", fieldValues=").append(mFieldValues);
389         }
390         if (mFieldContent != null) {
391             builder.append(", fieldContent=").append(mFieldContent);
392         }
393         if (mFieldPresentations != null) {
394             builder.append(", fieldPresentations=").append(mFieldPresentations.size());
395         }
396         if (mFieldDialogPresentations != null) {
397             builder.append(", fieldDialogPresentations=").append(mFieldDialogPresentations.size());
398         }
399         if (mFieldInlinePresentations != null) {
400             builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
401         }
402         if (mFieldInlineTooltipPresentations != null) {
403             builder.append(", fieldInlineTooltipInlinePresentations=").append(
404                     mFieldInlineTooltipPresentations.size());
405         }
406         if (mFieldFilters != null) {
407             builder.append(", fieldFilters=").append(mFieldFilters.size());
408         }
409         if (mPresentation != null) {
410             builder.append(", hasPresentation");
411         }
412         if (mDialogPresentation != null) {
413             builder.append(", hasDialogPresentation");
414         }
415         if (mInlinePresentation != null) {
416             builder.append(", hasInlinePresentation");
417         }
418         if (mInlineTooltipPresentation != null) {
419             builder.append(", hasInlineTooltipPresentation");
420         }
421         if (mAuthentication != null) {
422             builder.append(", hasAuthentication");
423         }
424         if (mCredentialFillInIntent != null) {
425             builder.append(", hasAuthenticationExtras");
426         }
427         if (mAutofillDatatypes != null) {
428             builder.append(", autofillDatatypes=").append(mAutofillDatatypes);
429         }
430         return builder.append(']').toString();
431     }
432 
433     /**
434      * Gets the id of this dataset.
435      *
436      * @return The id of this dataset or {@code null} if not set
437      *
438      * @hide
439      */
440     @TestApi
getId()441     public @Nullable String getId() {
442         return mId;
443     }
444 
445     /**
446      * Sets the reason as to why this dataset is eligible
447      * @hide
448      */
setEligibleReasonReason(@atasetEligibleReason int eligibleReason)449     public void setEligibleReasonReason(@DatasetEligibleReason int eligibleReason) {
450         this.mEligibleReason = eligibleReason;
451     }
452 
453     /**
454      * Get the reason as to why this dataset is eligible.
455      * @hide
456      */
getEligibleReason()457     public @DatasetEligibleReason int getEligibleReason() {
458         return mEligibleReason;
459     }
460 
461     /**
462      * A builder for {@link Dataset} objects. You must provide at least
463      * one value for a field or set an authentication intent.
464      */
465     public static final class Builder {
466         private ArrayList<AutofillId> mFieldIds = new ArrayList<>();
467         private ArrayList<AutofillValue> mFieldValues = new ArrayList();
468         private ArrayList<RemoteViews> mFieldPresentations = new ArrayList();
469         private ArrayList<RemoteViews> mFieldDialogPresentations = new ArrayList();
470         private ArrayList<InlinePresentation> mFieldInlinePresentations = new ArrayList();
471         private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations = new ArrayList();
472         private ArrayList<DatasetFieldFilter> mFieldFilters = new ArrayList();
473         private ArrayList<String> mAutofillDatatypes = new ArrayList();
474         @Nullable private ClipData mFieldContent;
475         private RemoteViews mPresentation;
476         private RemoteViews mDialogPresentation;
477         @Nullable private InlinePresentation mInlinePresentation;
478         @Nullable private InlinePresentation mInlineTooltipPresentation;
479         private IntentSender mAuthentication;
480 
481         private Intent mCredentialFillInIntent;
482         private boolean mDestroyed;
483         @Nullable private String mId;
484 
485         /**
486          * Usually, a field will be associated with a single autofill id and/or datatype.
487          * There could be null field value corresponding to different autofill ids or datatye
488          * values, but the implementation is ok with duplicating that information.
489          * This map is just for the purpose of optimization, to reduce the size of the pelled data
490          * over the binder transaction.
491          */
492         private ArrayMap<Field, Integer> mFieldToIndexdMap = new ArrayMap<>();
493 
494         /**
495          * Creates a new builder.
496          *
497          * @param presentation The presentation used to visualize this dataset.
498          * @deprecated Use {@link #Builder(Presentations)} instead.
499          */
500         @Deprecated
Builder(@onNull RemoteViews presentation)501         public Builder(@NonNull RemoteViews presentation) {
502             Objects.requireNonNull(presentation, "presentation must be non-null");
503             mPresentation = presentation;
504         }
505 
506         /**
507          * Creates a new builder.
508          *
509          * <p>Only called by augmented autofill.
510          *
511          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
512          *              as inline suggestions. If the dataset supports inline suggestions,
513          *              this should not be null.
514          * @hide
515          * @deprecated Use {@link #Builder(Presentations)} instead.
516          */
517         @SystemApi
518         @Deprecated
Builder(@onNull InlinePresentation inlinePresentation)519         public Builder(@NonNull InlinePresentation inlinePresentation) {
520             Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
521             mInlinePresentation = inlinePresentation;
522         }
523 
524         /**
525          * Creates a new builder.
526          *
527          * @param presentations The presentations used to visualize this dataset.
528          */
Builder(@onNull Presentations presentations)529         public Builder(@NonNull Presentations presentations) {
530             Objects.requireNonNull(presentations, "presentations must be non-null");
531 
532             mPresentation = presentations.getMenuPresentation();
533             mInlinePresentation = presentations.getInlinePresentation();
534             mInlineTooltipPresentation = presentations.getInlineTooltipPresentation();
535             mDialogPresentation = presentations.getDialogPresentation();
536         }
537 
538         /**
539          * Creates a new builder for a dataset where each field will be visualized independently.
540          *
541          * <p>When using this constructor, a presentation must be provided for each field through
542          * {@link #setField(AutofillId, Field)}.
543          */
Builder()544         public Builder() {
545         }
546 
547         /**
548          * Sets the {@link InlinePresentation} used to visualize this dataset as inline suggestions.
549          * If the dataset supports inline suggestions this should not be null.
550          *
551          * @throws IllegalStateException if {@link #build()} was already called.
552          *
553          * @return this builder.
554          * @deprecated Use {@link #Builder(Presentations)} instead.
555          */
556         @Deprecated
setInlinePresentation( @onNull InlinePresentation inlinePresentation)557         public @NonNull Builder setInlinePresentation(
558                 @NonNull InlinePresentation inlinePresentation) {
559             throwIfDestroyed();
560             Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
561             mInlinePresentation = inlinePresentation;
562             return this;
563         }
564 
565         /**
566          * Visualizes this dataset as inline suggestions.
567          *
568          * @param inlinePresentation the {@link InlinePresentation} used to visualize this
569          *         dataset as inline suggestions. If the dataset supports inline suggestions this
570          *         should not be null.
571          * @param inlineTooltipPresentation the {@link InlinePresentation} used to show
572          *         the tooltip for the {@code inlinePresentation}.
573          *
574          * @throws IllegalStateException if {@link #build()} was already called.
575          *
576          * @return this builder.
577          * @deprecated Use {@link #Builder(Presentations)} instead.
578          */
579         @Deprecated
setInlinePresentation( @onNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)580         public @NonNull Builder setInlinePresentation(
581                 @NonNull InlinePresentation inlinePresentation,
582                 @NonNull InlinePresentation inlineTooltipPresentation) {
583             throwIfDestroyed();
584             Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
585             Objects.requireNonNull(inlineTooltipPresentation,
586                     "inlineTooltipPresentation must be non-null");
587             mInlinePresentation = inlinePresentation;
588             mInlineTooltipPresentation = inlineTooltipPresentation;
589             return this;
590         }
591 
592         /**
593          * Triggers a custom UI before before autofilling the screen with the contents of this
594          * dataset.
595          *
596          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
597          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
598          * for examples.
599          *
600          * <p>This method is called when you need to provide an authentication
601          * UI for the data set. For example, when a data set contains credit card information
602          * (such as number, expiration date, and verification code), you can display UI
603          * asking for the verification code before filing in the data. Even if the
604          * data set is completely populated the system will launch the specified authentication
605          * intent and will need your approval to fill it in. Since the data set is "locked"
606          * until the user authenticates it, typically this data set name is masked
607          * (for example, "VISA....1234"). Typically you would want to store the data set
608          * labels non-encrypted and the actual sensitive data encrypted and not in memory.
609          * This allows showing the labels in the UI while involving the user if one of
610          * the items with these labels is chosen. Note that if you use sensitive data as
611          * a label, for example an email address, then it should also be encrypted.</p>
612          *
613          * <p>When a user triggers autofill, the system launches the provided intent
614          * whose extras will have the {@link
615          * android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen content},
616          * and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE client
617          * state}. Once you complete your authentication flow you should set the activity
618          * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated
619          * {@link Dataset dataset} or a fully-populated {@link FillResponse response} by
620          * setting it to the {@link
621          * android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you
622          * provide a dataset in the result, it will replace the authenticated dataset and
623          * will be immediately filled in. An exception to this behavior is if the original
624          * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset
625          * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then
626          * the original dataset will not be replaced,
627          * so that it can be triggered as a pending intent again.
628          * If you provide a response, it will replace the
629          * current response and the UI will be refreshed. For example, if you provided
630          * credit card information without the CVV for the data set in the {@link FillResponse
631          * response} then the returned data set should contain the CVV entry.
632          *
633          * <p><b>Note:</b> Do not make the provided pending intent
634          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
635          * platform needs to fill in the authentication arguments.
636          *
637          * @param authentication Intent to an activity with your authentication flow.
638          *
639          * @throws IllegalStateException if {@link #build()} was already called.
640          *
641          * @return this builder.
642          *
643          * @see android.app.PendingIntent
644          */
setAuthentication(@ullable IntentSender authentication)645         public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
646             throwIfDestroyed();
647             mAuthentication = authentication;
648             return this;
649         }
650 
651         /**
652          * Sets extras to be associated with the {@code authentication} intent sender, to be
653          * set on the intent that is fired through the intent sender.
654          *
655          * Autofill providers can set any extras they wish to receive directly on the intent
656          * that is used to create the {@code authentication}. This is an internal API, to be
657          * used by the platform to associate data with a given dataset. These extras will be
658          * merged with the {@code clientState} and sent as part of the fill in intent when
659          * the {@code authentication} intentSender is invoked.
660          *
661          * @hide
662          */
663         @Hide
setCredentialFillInIntent(@ullable Intent credentialFillInIntent)664         public @NonNull Builder setCredentialFillInIntent(@Nullable Intent credentialFillInIntent) {
665             throwIfDestroyed();
666             mCredentialFillInIntent = credentialFillInIntent;
667             return this;
668         }
669 
670         /**
671          * Sets the id for the dataset so its usage can be tracked.
672          *
673          * <p>Dataset usage can be tracked for 2 purposes:
674          *
675          * <ul>
676          *   <li>For statistical purposes, the service can call
677          * {@link AutofillService#getFillEventHistory()} when handling {@link
678          * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
679          * calls.
680          *   <li>For normal autofill workflow, the service can call
681          *   {@link SaveRequest#getDatasetIds()} when handling
682          *   {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} calls.
683          * </ul>
684          *
685          * @param id id for this dataset or {@code null} to unset.
686          *
687          * @throws IllegalStateException if {@link #build()} was already called.
688          *
689          * @return this builder.
690          */
setId(@ullable String id)691         public @NonNull Builder setId(@Nullable String id) {
692             throwIfDestroyed();
693             mId = id;
694             return this;
695         }
696 
697         /**
698          * Sets the content for a field.
699          *
700          * <p>Only called by augmented autofill.
701          *
702          * <p>For a given field, either a {@link AutofillValue value} or content can be filled, but
703          * not both. Furthermore, when filling content, only a single field can be filled.
704          *
705          * <p>The provided {@link ClipData} can contain content URIs (e.g. a URI for an image).
706          * The augmented autofill provider setting the content here must itself have at least
707          * read permissions to any passed content URIs. If the user accepts the suggestion backed
708          * by the content URI(s), the platform will automatically grant read URI permissions to
709          * the app being autofilled, just before passing the content URI(s) to it. The granted
710          * permissions will be transient and tied to the lifecycle of the activity being filled
711          * (when the activity finishes, permissions will automatically be revoked by the platform).
712          *
713          * @param id id returned by
714          * {@link android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
715          * @param content content to be autofilled. Pass {@code null} if you do not have the content
716          * but the target view is a logical part of the dataset. For example, if the dataset needs
717          * authentication.
718          *
719          * @throws IllegalStateException if {@link #build()} was already called.
720          * @throws IllegalArgumentException if the provided content
721          * {@link ClipData.Item#getIntent() contains an intent}
722          *
723          * @return this builder.
724          *
725          * @hide
726          */
727         @TestApi
728         @SystemApi
729         @SuppressLint("MissingGetterMatchingBuilder")
setContent(@onNull AutofillId id, @Nullable ClipData content)730         public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) {
731             throwIfDestroyed();
732             if (content != null) {
733                 for (int i = 0; i < content.getItemCount(); i++) {
734                     Preconditions.checkArgument(content.getItemAt(i).getIntent() == null,
735                             "Content items cannot contain an Intent: content=" + content);
736                 }
737             }
738             setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
739             mFieldContent = content;
740             return this;
741         }
742 
743         /**
744          * Sets the value of a field.
745          *
746          * <b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, this method would
747          * throw an {@link IllegalStateException} if this builder was constructed without a
748          * {@link RemoteViews presentation}. Android {@link android.os.Build.VERSION_CODES#P} and
749          * higher removed this restriction because datasets used as an
750          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT
751          * authentication result} do not need a presentation. But if you don't set the presentation
752          * in the constructor in a dataset that is meant to be shown to the user, the autofill UI
753          * for this field will not be displayed.
754          *
755          * <p><b>Note:</b> On Android {@link android.os.Build.VERSION_CODES#P} and
756          * higher, datasets that require authentication can be also be filtered by passing a
757          * {@link AutofillValue#forText(CharSequence) text value} as the {@code value} parameter.
758          *
759          * @param id id returned by {@link
760          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
761          * @param value value to be autofilled. Pass {@code null} if you do not have the value
762          *        but the target view is a logical part of the dataset. For example, if
763          *        the dataset needs authentication and you have no access to the value.
764          *
765          * @throws IllegalStateException if {@link #build()} was already called.
766          *
767          * @return this builder.
768          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
769          */
770         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value)771         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
772             throwIfDestroyed();
773             setLifeTheUniverseAndEverything(id, value, null, null, null, null, null);
774             return this;
775         }
776 
777         /**
778          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
779          * visualize it.
780          *
781          * <p><b>Note:</b> On Android {@link android.os.Build.VERSION_CODES#P} and
782          * higher, datasets that require authentication can be also be filtered by passing a
783          * {@link AutofillValue#forText(CharSequence) text value} as the  {@code value} parameter.
784          *
785          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
786          * or background color: Autofill on different platforms may have different themes.
787          *
788          * @param id id returned by {@link
789          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
790          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
791          *        but the target view is a logical part of the dataset. For example, if
792          *        the dataset needs authentication and you have no access to the value.
793          * @param presentation the presentation used to visualize this field.
794          *
795          * @throws IllegalStateException if {@link #build()} was already called.
796          *
797          * @return this builder.
798          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
799          */
800         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation)801         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
802                 @NonNull RemoteViews presentation) {
803             throwIfDestroyed();
804             Objects.requireNonNull(presentation, "presentation cannot be null");
805             setLifeTheUniverseAndEverything(id, value, presentation, null, null, null, null);
806             return this;
807         }
808 
809         /**
810          * Sets the value of a field using an <a href="#Filtering">explicit filter</a>.
811          *
812          * <p>This method is typically used when the dataset requires authentication and the service
813          * does not know its value but wants to hide the dataset after the user enters a minimum
814          * number of characters. For example, if the dataset represents a credit card number and the
815          * service does not want to show the "Tap to authenticate" message until the user tapped
816          * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}.
817          *
818          * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
819          * value it's easier to filter by calling {@link #setValue(AutofillId, AutofillValue)} and
820          * use the value to filter.
821          *
822          * @param id id returned by {@link
823          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
824          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
825          *        but the target view is a logical part of the dataset. For example, if
826          *        the dataset needs authentication and you have no access to the value.
827          * @param filter regex used to determine if the dataset should be shown in the autofill UI;
828          *        when {@code null}, it disables filtering on that dataset (this is the recommended
829          *        approach when {@code value} is not {@code null} and field contains sensitive data
830          *        such as passwords).
831          *
832          * @return this builder.
833          * @throws IllegalStateException if the builder was constructed without a
834          *         {@link RemoteViews presentation} or {@link #build()} was already called.
835          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
836          */
837         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter)838         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
839                 @Nullable Pattern filter) {
840             throwIfDestroyed();
841             Preconditions.checkState(mPresentation != null,
842                     "Dataset presentation not set on constructor");
843             setLifeTheUniverseAndEverything(
844                     id, value, null, null, null, new DatasetFieldFilter(filter), null);
845             return this;
846         }
847 
848         /**
849          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
850          * visualize it and a <a href="#Filtering">explicit filter</a>.
851          *
852          * <p>This method is typically used when the dataset requires authentication and the service
853          * does not know its value but wants to hide the dataset after the user enters a minimum
854          * number of characters. For example, if the dataset represents a credit card number and the
855          * service does not want to show the "Tap to authenticate" message until the user tapped
856          * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}.
857          *
858          * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
859          * value it's easier to filter by calling
860          * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
861          *
862          * @param id id returned by {@link
863          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
864          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
865          *        but the target view is a logical part of the dataset. For example, if
866          *        the dataset needs authentication and you have no access to the value.
867          * @param filter regex used to determine if the dataset should be shown in the autofill UI;
868          *        when {@code null}, it disables filtering on that dataset (this is the recommended
869          *        approach when {@code value} is not {@code null} and field contains sensitive data
870          *        such as passwords).
871          * @param presentation the presentation used to visualize this field.
872          *
873          * @throws IllegalStateException if {@link #build()} was already called.
874          *
875          * @return this builder.
876          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
877          */
878         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation)879         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
880                 @Nullable Pattern filter, @NonNull RemoteViews presentation) {
881             throwIfDestroyed();
882             Objects.requireNonNull(presentation, "presentation cannot be null");
883             setLifeTheUniverseAndEverything(id, value, presentation, null, null,
884                     new DatasetFieldFilter(filter), null);
885             return this;
886         }
887 
888         /**
889          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
890          * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion.
891          *
892          * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
893          * value it's easier to filter by calling
894          * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
895          *
896          * @param id id returned by {@link
897          *        android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
898          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
899          *        but the target view is a logical part of the dataset. For example, if
900          *        the dataset needs authentication and you have no access to the value.
901          * @param presentation the presentation used to visualize this field.
902          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
903          *        as inline suggestions. If the dataset supports inline suggestions,
904          *        this should not be null.
905          *
906          * @throws IllegalStateException if {@link #build()} was already called.
907          *
908          * @return this builder.
909          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
910          */
911         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation)912         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
913                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
914             throwIfDestroyed();
915             Objects.requireNonNull(presentation, "presentation cannot be null");
916             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
917             setLifeTheUniverseAndEverything(
918                     id, value, presentation, inlinePresentation, null, null, null);
919             return this;
920         }
921 
922         /**
923          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
924          * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion.
925          *
926          * @see #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)
927          *
928          * @param id id returned by {@link
929          *        android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
930          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
931          *        but the target view is a logical part of the dataset. For example, if
932          *        the dataset needs authentication and you have no access to the value.
933          * @param presentation the presentation used to visualize this field.
934          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
935          *        as inline suggestions. If the dataset supports inline suggestions,
936          *        this should not be null.
937          * @param inlineTooltipPresentation The {@link InlinePresentation} used to show
938          *        the tooltip for the {@code inlinePresentation}.
939          *
940          * @throws IllegalStateException if {@link #build()} was already called.
941          *
942          * @return this builder.
943          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
944          */
945         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)946         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
947                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
948                 @NonNull InlinePresentation inlineTooltipPresentation) {
949             throwIfDestroyed();
950             Objects.requireNonNull(presentation, "presentation cannot be null");
951             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
952             Objects.requireNonNull(inlineTooltipPresentation,
953                     "inlineTooltipPresentation cannot be null");
954             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
955                     inlineTooltipPresentation, null, null);
956             return this;
957         }
958 
959         /**
960          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
961          * visualize it and a <a href="#Filtering">explicit filter</a>, and an
962          * {@link InlinePresentation} to visualize it as an inline suggestion.
963          *
964          * <p>This method is typically used when the dataset requires authentication and the service
965          * does not know its value but wants to hide the dataset after the user enters a minimum
966          * number of characters. For example, if the dataset represents a credit card number and the
967          * service does not want to show the "Tap to authenticate" message until the user tapped
968          * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}.
969          *
970          * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
971          * value it's easier to filter by calling
972          * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
973          *
974          * @param id id returned by {@link
975          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
976          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
977          *        but the target view is a logical part of the dataset. For example, if
978          *        the dataset needs authentication and you have no access to the value.
979          * @param filter regex used to determine if the dataset should be shown in the autofill UI;
980          *        when {@code null}, it disables filtering on that dataset (this is the recommended
981          *        approach when {@code value} is not {@code null} and field contains sensitive data
982          *        such as passwords).
983          * @param presentation the presentation used to visualize this field.
984          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
985          *        as inline suggestions. If the dataset supports inline suggestions, this
986          *        should not be null.
987          *
988          * @throws IllegalStateException if {@link #build()} was already called.
989          *
990          * @return this builder.
991          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
992          */
993         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation)994         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
995                 @Nullable Pattern filter, @NonNull RemoteViews presentation,
996                 @NonNull InlinePresentation inlinePresentation) {
997             throwIfDestroyed();
998             Objects.requireNonNull(presentation, "presentation cannot be null");
999             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
1000             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
1001                     new DatasetFieldFilter(filter), null);
1002             return this;
1003         }
1004 
1005         /**
1006          * Sets the value of a field, using a custom {@link RemoteViews presentation} to
1007          * visualize it and a <a href="#Filtering">explicit filter</a>, and an
1008          * {@link InlinePresentation} to visualize it as an inline suggestion.
1009          *
1010          * @see #setValue(AutofillId, AutofillValue, Pattern, RemoteViews, InlinePresentation)
1011          *
1012          * @param id id returned by {@link
1013          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
1014          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
1015          *        but the target view is a logical part of the dataset. For example, if
1016          *        the dataset needs authentication and you have no access to the value.
1017          * @param filter regex used to determine if the dataset should be shown in the autofill UI;
1018          *        when {@code null}, it disables filtering on that dataset (this is the recommended
1019          *        approach when {@code value} is not {@code null} and field contains sensitive data
1020          *        such as passwords).
1021          * @param presentation the presentation used to visualize this field.
1022          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
1023          *        as inline suggestions. If the dataset supports inline suggestions, this
1024          *        should not be null.
1025          * @param inlineTooltipPresentation The {@link InlinePresentation} used to show
1026          *        the tooltip for the {@code inlinePresentation}.
1027          *
1028          * @throws IllegalStateException if {@link #build()} was already called.
1029          *
1030          * @return this builder.
1031          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
1032          */
1033         @Deprecated
setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)1034         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
1035                 @Nullable Pattern filter, @NonNull RemoteViews presentation,
1036                 @NonNull InlinePresentation inlinePresentation,
1037                 @NonNull InlinePresentation inlineTooltipPresentation) {
1038             throwIfDestroyed();
1039             Objects.requireNonNull(presentation, "presentation cannot be null");
1040             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
1041             Objects.requireNonNull(inlineTooltipPresentation,
1042                     "inlineTooltipPresentation cannot be null");
1043             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
1044                     inlineTooltipPresentation, new DatasetFieldFilter(filter), null);
1045             return this;
1046         }
1047 
1048         /**
1049          * Sets the value of a field.
1050          *
1051          * Before Android 13, this information could be provided using several overloaded
1052          * setValue(...) methods. This method replaces those with a Builder pattern.
1053          * For example, in the old workflow, the app sets a field would be:
1054          * <pre class="prettyprint">
1055          *  Dataset.Builder dataset = new Dataset.Builder();
1056          *  if (filter != null) {
1057          *      if (presentation != null) {
1058          *          if (inlinePresentation != null) {
1059          *              dataset.setValue(id, value, filter, presentation, inlinePresentation)
1060          *          } else {
1061          *              dataset.setValue(id, value, filter, presentation);
1062          *          }
1063          *      } else {
1064          *          dataset.setValue(id, value, filter);
1065          *      }
1066          *  } else {
1067          *      if (presentation != null) {
1068          *          if (inlinePresentation != null) {
1069          *              dataset.setValue(id, value, presentation, inlinePresentation)
1070          *          } else {
1071          *              dataset.setValue(id, value, presentation);
1072          *          }
1073          *      } else {
1074          *          dataset.setValue(id, value);
1075          *      }
1076          *  }
1077          *  </pre>
1078          * <p>The new workflow would be:
1079          * <pre class="prettyprint">
1080          * Field.Builder fieldBuilder = new Field.Builder();
1081          * if (value != null) {
1082          *     fieldBuilder.setValue(value);
1083          * }
1084          * if (filter != null) {
1085          *     fieldBuilder.setFilter(filter);
1086          * }
1087          * Presentations.Builder presentationsBuilder = new Presentations.Builder();
1088          * if (presentation != null) {
1089          *     presentationsBuilder.setMenuPresentation(presentation);
1090          * }
1091          * if (inlinePresentation != null) {
1092          *     presentationsBuilder.setInlinePresentation(inlinePresentation);
1093          * }
1094          * if (dialogPresentation != null) {
1095          *     presentationsBuilder.setDialogPresentation(dialogPresentation);
1096          * }
1097          * fieldBuilder.setPresentations(presentationsBuilder.build());
1098          * dataset.setField(id, fieldBuilder.build());
1099          * </pre>
1100          *
1101          * @see Field
1102          *
1103          * @param id id returned by {@link
1104          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
1105          * @param field the fill information about the field.
1106          *
1107          * @throws IllegalStateException if {@link #build()} was already called.
1108          *
1109          * @return this builder.
1110          */
setField(@onNull AutofillId id, @Nullable Field field)1111         public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
1112             throwIfDestroyed();
1113 
1114             if (mFieldToIndexdMap.containsKey(field)) {
1115                 int index = mFieldToIndexdMap.get(field);
1116                 if (mFieldIds.get(index) == null) {
1117                     mFieldIds.set(index, id);
1118                     return this;
1119                 }
1120                 // if the Autofill Id is already set, ignore and proceed as if setting in a new
1121                 // value.
1122             }
1123             int index;
1124             if (field == null) {
1125                 index = setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
1126             } else {
1127                 final DatasetFieldFilter filter = field.getDatasetFieldFilter();
1128                 final Presentations presentations = field.getPresentations();
1129                 if (presentations == null) {
1130                     index = setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
1131                             filter, null);
1132                 } else {
1133                     index = setLifeTheUniverseAndEverything(id, field.getValue(),
1134                             presentations.getMenuPresentation(),
1135                             presentations.getInlinePresentation(),
1136                             presentations.getInlineTooltipPresentation(), filter,
1137                             presentations.getDialogPresentation());
1138                 }
1139             }
1140             mFieldToIndexdMap.put(field, index);
1141             return this;
1142         }
1143 
1144         /**
1145          * Adds a field to this Dataset with a specific type. This is used to send back Field
1146          * information when Autofilling with platform detections is on.
1147          * Platform detections are on when receiving a populated list from
1148          * FillRequest#getHints().
1149          *
1150          * Populate every field/type known for this user for this app.
1151          *
1152          * For example, if getHints() contains "username" and "password",
1153          * a new Dataset should be created that calls this method twice,
1154          * one for the username, then another for the password (assuming
1155          * the only one credential pair is found for the user). If a user
1156          * has two credential pairs, then two Datasets should be created,
1157          * and so on.
1158          *
1159          * @param hint An autofill hint returned from {@link
1160          *         FillRequest#getHints()}.
1161          *
1162          * @param field the fill information about the field.
1163          *
1164          * @throws IllegalStateException if {@link #build()} was already called
1165          * or this builder also contains AutofillId information
1166          *
1167          * @return this builder.
1168          */
setField(@onNull String hint, @NonNull Field field)1169         public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) {
1170             throwIfDestroyed();
1171 
1172             if (mFieldToIndexdMap.containsKey(field)) {
1173                 int index = mFieldToIndexdMap.get(field);
1174                 if (mAutofillDatatypes.get(index) == null) {
1175                     mAutofillDatatypes.set(index, hint);
1176                     return this;
1177                 }
1178                 // if the hint is already set, ignore and proceed as if setting in a new hint.
1179             }
1180 
1181             int index;
1182             final DatasetFieldFilter filter = field.getDatasetFieldFilter();
1183             final Presentations presentations = field.getPresentations();
1184             if (presentations == null) {
1185                 index = setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
1186                         filter, null);
1187             } else {
1188                 index = setLifeTheUniverseAndEverything(hint, field.getValue(),
1189                         presentations.getMenuPresentation(),
1190                         presentations.getInlinePresentation(),
1191                         presentations.getInlineTooltipPresentation(), filter,
1192                         presentations.getDialogPresentation());
1193             }
1194             mFieldToIndexdMap.put(field, index);
1195             return this;
1196         }
1197 
1198         /**
1199          * Adds a field to this Dataset that is relevant to all applicable hints. This is used to
1200          * provide field information when autofill with platform detections is enabled.
1201          * Platform detections are on when receiving a populated list from
1202          * FillRequest#getHints().
1203          *
1204          * @param field the fill information about the field.
1205          *
1206          * @throws IllegalStateException if {@link #build()} was already called
1207          * or this builder also contains AutofillId information
1208          *
1209          * @return this builder.
1210          */
setFieldForAllHints(@onNull Field field)1211         public @NonNull Dataset.Builder setFieldForAllHints(@NonNull Field field) {
1212             return setField(AutofillManager.ANY_HINT, field);
1213         }
1214 
1215         /**
1216          * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
1217          * {@link InlinePresentation} to visualize it as an inline suggestion.
1218          *
1219          * <p>Only called by augmented autofill.
1220          *
1221          * @param id id returned by {@link
1222          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
1223          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
1224          *        but the target view is a logical part of the dataset. For example, if
1225          *        the dataset needs authentication and you have no access to the value.
1226          * @param filter regex used to determine if the dataset should be shown in the autofill UI;
1227          *        when {@code null}, it disables filtering on that dataset (this is the recommended
1228          *        approach when {@code value} is not {@code null} and field contains sensitive data
1229          *        such as passwords).
1230          * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
1231          *        as inline suggestions. If the dataset supports inline suggestions, this
1232          *        should not be null.
1233          *
1234          * @throws IllegalStateException if {@link #build()} was already called.
1235          *
1236          * @return this builder.
1237          * @deprecated Use {@link #setField(AutofillId, Field)} instead.
1238          * @hide
1239          */
1240         @Deprecated
1241         @SystemApi
setFieldInlinePresentation(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull InlinePresentation inlinePresentation)1242         public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
1243                 @Nullable AutofillValue value, @Nullable Pattern filter,
1244                 @NonNull InlinePresentation inlinePresentation) {
1245             throwIfDestroyed();
1246             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
1247             setLifeTheUniverseAndEverything(id, value, null, inlinePresentation, null,
1248                     new DatasetFieldFilter(filter), null);
1249             return this;
1250         }
1251 
1252         /** Returns the index at which this id was modified or inserted */
setLifeTheUniverseAndEverything(@onNull String datatype, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1253         private int setLifeTheUniverseAndEverything(@NonNull String datatype,
1254                 @Nullable AutofillValue value,
1255                 @Nullable RemoteViews presentation,
1256                 @Nullable InlinePresentation inlinePresentation,
1257                 @Nullable InlinePresentation tooltip,
1258                 @Nullable DatasetFieldFilter filter,
1259                 @Nullable RemoteViews dialogPresentation) {
1260             Objects.requireNonNull(datatype, "datatype cannot be null");
1261             final int existingIdx = mAutofillDatatypes.indexOf(datatype);
1262             if (existingIdx >= 0) {
1263                 mAutofillDatatypes.add(datatype);
1264                 mFieldValues.set(existingIdx, value);
1265                 mFieldPresentations.set(existingIdx, presentation);
1266                 mFieldDialogPresentations.set(existingIdx, dialogPresentation);
1267                 mFieldInlinePresentations.set(existingIdx, inlinePresentation);
1268                 mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
1269                 mFieldFilters.set(existingIdx, filter);
1270                 return existingIdx;
1271             }
1272             mFieldIds.add(null);
1273             mAutofillDatatypes.add(datatype);
1274             mFieldValues.add(value);
1275             mFieldPresentations.add(presentation);
1276             mFieldDialogPresentations.add(dialogPresentation);
1277             mFieldInlinePresentations.add(inlinePresentation);
1278             mFieldInlineTooltipPresentations.add(tooltip);
1279             mFieldFilters.add(filter);
1280             return mFieldIds.size() - 1;
1281         }
1282 
1283         /** Returns the index at which this id was modified or inserted */
setLifeTheUniverseAndEverything(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1284         private int setLifeTheUniverseAndEverything(@NonNull AutofillId id,
1285                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
1286                 @Nullable InlinePresentation inlinePresentation,
1287                 @Nullable InlinePresentation tooltip,
1288                 @Nullable DatasetFieldFilter filter,
1289                 @Nullable RemoteViews dialogPresentation) {
1290             Objects.requireNonNull(id, "id cannot be null");
1291             final int existingIdx = mFieldIds.indexOf(id);
1292             if (existingIdx >= 0) {
1293                 mFieldValues.set(existingIdx, value);
1294                 mFieldPresentations.set(existingIdx, presentation);
1295                 mFieldDialogPresentations.set(existingIdx, dialogPresentation);
1296                 mFieldInlinePresentations.set(existingIdx, inlinePresentation);
1297                 mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
1298                 mFieldFilters.set(existingIdx, filter);
1299                 return existingIdx;
1300             }
1301             mFieldIds.add(id);
1302             mAutofillDatatypes.add(null);
1303             mFieldValues.add(value);
1304             mFieldPresentations.add(presentation);
1305             mFieldDialogPresentations.add(dialogPresentation);
1306             mFieldInlinePresentations.add(inlinePresentation);
1307             mFieldInlineTooltipPresentations.add(tooltip);
1308             mFieldFilters.add(filter);
1309             return mFieldIds.size() - 1;
1310         }
1311 
createFromParcel( @ullable AutofillId id, @Nullable String datatype, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1312         private void createFromParcel(
1313                 @Nullable AutofillId id, @Nullable String datatype,
1314                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
1315                 @Nullable InlinePresentation inlinePresentation,
1316                 @Nullable InlinePresentation tooltip,
1317                 @Nullable DatasetFieldFilter filter,
1318                 @Nullable RemoteViews dialogPresentation) {
1319             if (id != null) {
1320                 final int existingIdx = mFieldIds.indexOf(id);
1321                 if (existingIdx >= 0) {
1322                     mFieldValues.set(existingIdx, value);
1323                     mFieldPresentations.set(existingIdx, presentation);
1324                     mFieldDialogPresentations.set(existingIdx, dialogPresentation);
1325                     mFieldInlinePresentations.set(existingIdx, inlinePresentation);
1326                     mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
1327                     mFieldFilters.set(existingIdx, filter);
1328                     return;
1329                 }
1330             }
1331             mFieldIds.add(id);
1332             mAutofillDatatypes.add(datatype);
1333             mFieldValues.add(value);
1334             mFieldPresentations.add(presentation);
1335             mFieldDialogPresentations.add(dialogPresentation);
1336             mFieldInlinePresentations.add(inlinePresentation);
1337             mFieldInlineTooltipPresentations.add(tooltip);
1338             mFieldFilters.add(filter);
1339             return;
1340         }
1341 
1342         /**
1343          * Creates a new {@link Dataset} instance.
1344          *
1345          * <p>You should not interact with this builder once this method is called.
1346          *
1347          * @throws IllegalStateException if no field was set (through
1348          * {@link #setField(AutofillId, Field)}), or if {@link #build()} was already called.
1349          *
1350          * @return The built dataset.
1351          */
build()1352         public @NonNull Dataset build() {
1353             throwIfDestroyed();
1354             mDestroyed = true;
1355             if (mFieldIds == null && mAutofillDatatypes == null) {
1356                 throw new IllegalStateException("at least one of field or datatype must be set");
1357             }
1358             if (mFieldIds != null && mAutofillDatatypes != null) {
1359                 if (mFieldIds.size() == 0 && mAutofillDatatypes.size() == 0) {
1360                     throw new IllegalStateException(
1361                             "at least one of field or datatype must be set");
1362                 }
1363             }
1364             if (mFieldContent != null) {
1365                 if (mFieldIds.size() > 1) {
1366                     throw new IllegalStateException(
1367                             "when filling content, only one field can be filled");
1368                 }
1369                 if (mFieldValues.get(0) != null) {
1370                     throw new IllegalStateException("cannot fill both content and values");
1371                 }
1372             }
1373             return new Dataset(this);
1374         }
1375 
throwIfDestroyed()1376         private void throwIfDestroyed() {
1377             if (mDestroyed) {
1378                 throw new IllegalStateException("Already called #build()");
1379             }
1380         }
1381     }
1382 
1383     /////////////////////////////////////
1384     //  Parcelable "contract" methods. //
1385     /////////////////////////////////////
1386 
1387     @Override
describeContents()1388     public int describeContents() {
1389         return 0;
1390     }
1391 
1392     @Override
writeToParcel(Parcel parcel, int flags)1393     public void writeToParcel(Parcel parcel, int flags) {
1394         parcel.writeParcelable(mPresentation, flags);
1395         parcel.writeParcelable(mDialogPresentation, flags);
1396         parcel.writeParcelable(mInlinePresentation, flags);
1397         parcel.writeParcelable(mInlineTooltipPresentation, flags);
1398         parcel.writeTypedList(mFieldIds, flags);
1399         parcel.writeTypedList(mFieldValues, flags);
1400         parcel.writeTypedList(mFieldPresentations, flags);
1401         parcel.writeTypedList(mFieldDialogPresentations, flags);
1402         parcel.writeTypedList(mFieldInlinePresentations, flags);
1403         parcel.writeTypedList(mFieldInlineTooltipPresentations, flags);
1404         parcel.writeTypedList(mFieldFilters, flags);
1405         parcel.writeStringList(mAutofillDatatypes);
1406         parcel.writeParcelable(mFieldContent, flags);
1407         parcel.writeParcelable(mAuthentication, flags);
1408         parcel.writeString(mId);
1409         parcel.writeInt(mEligibleReason);
1410         parcel.writeTypedObject(mCredentialFillInIntent, flags);
1411     }
1412 
1413     public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
1414         @Override
1415         public Dataset createFromParcel(Parcel parcel) {
1416             final RemoteViews presentation = parcel.readParcelable(null,
1417                     android.widget.RemoteViews.class);
1418             final RemoteViews dialogPresentation = parcel.readParcelable(null,
1419                     android.widget.RemoteViews.class);
1420             final InlinePresentation inlinePresentation = parcel.readParcelable(null,
1421                     android.service.autofill.InlinePresentation.class);
1422             final InlinePresentation inlineTooltipPresentation =
1423                     parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
1424             final ArrayList<AutofillId> ids =
1425                     parcel.createTypedArrayList(AutofillId.CREATOR);
1426             final ArrayList<AutofillValue> values =
1427                     parcel.createTypedArrayList(AutofillValue.CREATOR);
1428             final ArrayList<RemoteViews> presentations =
1429                     parcel.createTypedArrayList(RemoteViews.CREATOR);
1430             final ArrayList<RemoteViews> dialogPresentations =
1431                     parcel.createTypedArrayList(RemoteViews.CREATOR);
1432             final ArrayList<InlinePresentation> inlinePresentations =
1433                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
1434             final ArrayList<InlinePresentation> inlineTooltipPresentations =
1435                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
1436             final ArrayList<DatasetFieldFilter> filters =
1437                     parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
1438             final ArrayList<String> autofillDatatypes =
1439                     parcel.createStringArrayList();
1440             final ClipData fieldContent = parcel.readParcelable(null,
1441                     android.content.ClipData.class);
1442             final IntentSender authentication = parcel.readParcelable(null,
1443                     android.content.IntentSender.class);
1444             final String datasetId = parcel.readString();
1445             final int eligibleReason = parcel.readInt();
1446             final Intent credentialFillInIntent = parcel.readTypedObject(Intent.CREATOR);
1447 
1448             // Always go through the builder to ensure the data ingested by
1449             // the system obeys the contract of the builder to avoid attacks
1450             // using specially crafted parcels.
1451             final Builder builder;
1452             if (presentation != null || inlinePresentation != null || dialogPresentation != null) {
1453                 final Presentations.Builder presentationsBuilder = new Presentations.Builder();
1454                 if (presentation != null) {
1455                     presentationsBuilder.setMenuPresentation(presentation);
1456                 }
1457                 if (inlinePresentation != null) {
1458                     presentationsBuilder.setInlinePresentation(inlinePresentation);
1459                 }
1460                 if (inlineTooltipPresentation != null) {
1461                     presentationsBuilder.setInlineTooltipPresentation(inlineTooltipPresentation);
1462                 }
1463                 if (dialogPresentation != null) {
1464                     presentationsBuilder.setDialogPresentation(dialogPresentation);
1465                 }
1466                 builder = new Builder(presentationsBuilder.build());
1467             } else {
1468                 builder = new Builder();
1469             }
1470 
1471             if (fieldContent != null) {
1472                 builder.setContent(ids.get(0), fieldContent);
1473             }
1474             final int inlinePresentationsSize = inlinePresentations.size();
1475             for (int i = 0; i < ids.size(); i++) {
1476                 final AutofillId id = ids.get(i);
1477                 final String datatype = autofillDatatypes.get(i);
1478                 final AutofillValue value = values.get(i);
1479                 final RemoteViews fieldPresentation = presentations.get(i);
1480                 final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
1481                 final InlinePresentation fieldInlinePresentation =
1482                         i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
1483                 final InlinePresentation fieldInlineTooltipPresentation =
1484                         i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
1485                 final DatasetFieldFilter filter = filters.get(i);
1486                 builder.createFromParcel(id, datatype, value, fieldPresentation,
1487                         fieldInlinePresentation, fieldInlineTooltipPresentation, filter,
1488                         fieldDialogPresentation);
1489             }
1490             builder.setAuthentication(authentication);
1491             builder.setCredentialFillInIntent(credentialFillInIntent);
1492             builder.setId(datasetId);
1493             Dataset dataset = builder.build();
1494             dataset.mEligibleReason = eligibleReason;
1495             return dataset;
1496         }
1497 
1498         @Override
1499         public Dataset[] newArray(int size) {
1500             return new Dataset[size];
1501         }
1502     };
1503 
1504     /**
1505      * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a
1506      * dataset field&dash; we cannot use a {@link Pattern} directly because then we wouldn't be
1507      * able to differentiate whether the service explicitly passed a {@code null} filter to disable
1508      * filter, or when it called the methods that does not take a filter {@link Pattern}.
1509      *
1510      * @hide
1511      */
1512     @TestApi
1513     public static final class DatasetFieldFilter implements Parcelable {
1514 
1515         /** @hide */
1516         @Nullable
1517         public final Pattern pattern;
1518 
DatasetFieldFilter(@ullable Pattern pattern)1519         DatasetFieldFilter(@Nullable Pattern pattern) {
1520             this.pattern = pattern;
1521         }
1522 
getPattern()1523         public @Nullable Pattern getPattern() {
1524             return pattern;
1525         }
1526 
1527         @Override
toString()1528         public String toString() {
1529             if (!sDebug) return super.toString();
1530 
1531             // Cannot log pattern because it could contain PII
1532             return pattern == null ? "null" : pattern.pattern().length() + "_chars";
1533         }
1534 
1535         @Override
describeContents()1536         public int describeContents() {
1537             return 0;
1538         }
1539 
1540         @Override
writeToParcel(@onNull Parcel parcel, int flags)1541         public void writeToParcel(@NonNull Parcel parcel, int flags) {
1542             parcel.writeSerializable(pattern);
1543         }
1544 
1545         @SuppressWarnings("hiding")
1546         public static final @android.annotation.NonNull Creator<DatasetFieldFilter> CREATOR =
1547                 new Creator<DatasetFieldFilter>() {
1548 
1549             @Override
1550             public DatasetFieldFilter createFromParcel(Parcel parcel) {
1551                 return new DatasetFieldFilter((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class));
1552             }
1553 
1554             @Override
1555             public DatasetFieldFilter[] newArray(int size) {
1556                 return new DatasetFieldFilter[size];
1557             }
1558         };
1559     }
1560 }
1561