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.service.autofill.AutofillServiceHelper.assertValid;
20 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
21 import static android.view.autofill.Helper.sDebug;
22 
23 import android.annotation.DrawableRes;
24 import android.annotation.IntDef;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.StringRes;
28 import android.annotation.SuppressLint;
29 import android.annotation.TestApi;
30 import android.app.Activity;
31 import android.app.PendingIntent;
32 import android.content.Intent;
33 import android.content.IntentSender;
34 import android.content.pm.ParceledListSlice;
35 import android.os.Bundle;
36 import android.os.Parcel;
37 import android.os.Parcelable;
38 import android.service.assist.classification.FieldClassification;
39 import android.view.autofill.AutofillId;
40 import android.widget.RemoteViews;
41 
42 import com.android.internal.util.Preconditions;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.List;
49 import java.util.Objects;
50 import java.util.Set;
51 
52 /**
53  * Response for an {@link
54  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
55  *
56  * <p>See the main {@link AutofillService} documentation for more details and examples.
57  */
58 public final class FillResponse implements Parcelable {
59     // common_typos_disable
60 
61     /**
62      * Flag used to generate {@link FillEventHistory.Event events} of type
63      * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}&mdash;if this flag is not passed to
64      * {@link Builder#setFlags(int)}, these events are not generated.
65      */
66     public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1;
67 
68     /**
69      * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}&mdash;
70      * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the
71      * activiy that generated the {@link FillRequest}, not the whole app.
72      */
73     public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
74 
75     /**
76      * Flag used to request to wait for a delayed fill from the remote Autofill service if it's
77      * passed to {@link Builder#setFlags(int)}.
78      *
79      * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send
80      * a {@link FillResponse} to the latest {@link FillRequest} via
81      * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback}
82      * has timed out.
83      */
84     public static final int FLAG_DELAY_FILL = 0x4;
85 
86     /**
87      * @hide
88      */
89     public static final int FLAG_CREDENTIAL_MANAGER_RESPONSE = 0x8;
90 
91     /** @hide */
92     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
93             FLAG_TRACK_CONTEXT_COMMITED,
94             FLAG_DISABLE_ACTIVITY_ONLY,
95             FLAG_DELAY_FILL,
96             FLAG_CREDENTIAL_MANAGER_RESPONSE
97     })
98     @Retention(RetentionPolicy.SOURCE)
99     @interface FillResponseFlags {}
100 
101     private final @Nullable ParceledListSlice<Dataset> mDatasets;
102     private final @Nullable SaveInfo mSaveInfo;
103     private final @Nullable Bundle mClientState;
104     private final @Nullable RemoteViews mPresentation;
105     private final @Nullable InlinePresentation mInlinePresentation;
106     private final @Nullable InlinePresentation mInlineTooltipPresentation;
107     private final @Nullable RemoteViews mDialogPresentation;
108     private final @Nullable RemoteViews mDialogHeader;
109     private final @Nullable RemoteViews mHeader;
110     private final @Nullable RemoteViews mFooter;
111     private final @Nullable IntentSender mAuthentication;
112     private final @Nullable AutofillId[] mAuthenticationIds;
113     private final @Nullable AutofillId[] mIgnoredIds;
114     private final @Nullable AutofillId[] mFillDialogTriggerIds;
115     private final long mDisableDuration;
116     private final @Nullable AutofillId[] mFieldClassificationIds;
117     private final int mFlags;
118     private int mRequestId;
119     private final @Nullable UserData mUserData;
120     private final @Nullable int[] mCancelIds;
121     private final boolean mSupportsInlineSuggestions;
122     private final @DrawableRes int mIconResourceId;
123     private final @StringRes int mServiceDisplayNameResourceId;
124     private final boolean mShowFillDialogIcon;
125     private final boolean mShowSaveDialogIcon;
126     private final @Nullable FieldClassification[] mDetectedFieldTypes;
127     private final @Nullable PendingIntent mDialogPendingIntent;
128 
129     /**
130     * Creates a shollow copy of the provided FillResponse.
131     *
132     * @hide
133     */
shallowCopy( FillResponse r, List<Dataset> datasets, SaveInfo saveInfo)134     public static FillResponse shallowCopy(
135             FillResponse r, List<Dataset> datasets, SaveInfo saveInfo) {
136         return new FillResponse(
137                 (datasets != null) ? new ParceledListSlice<>(datasets) : null,
138                 saveInfo,
139                 r.mClientState,
140                 r.mPresentation,
141                 r.mInlinePresentation,
142                 r.mInlineTooltipPresentation,
143                 r.mDialogPresentation,
144                 r.mDialogHeader,
145                 r.mHeader,
146                 r.mFooter,
147                 r.mAuthentication,
148                 r.mAuthenticationIds,
149                 r.mIgnoredIds,
150                 r.mFillDialogTriggerIds,
151                 r.mDisableDuration,
152                 r.mFieldClassificationIds,
153                 r.mFlags,
154                 r.mRequestId,
155                 r.mUserData,
156                 r.mCancelIds,
157                 r.mSupportsInlineSuggestions,
158                 r.mIconResourceId,
159                 r.mServiceDisplayNameResourceId,
160                 r.mShowFillDialogIcon,
161                 r.mShowSaveDialogIcon,
162                 r.mDetectedFieldTypes,
163                 r.mDialogPendingIntent);
164     }
165 
FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState, RemoteViews presentation, InlinePresentation inlinePresentation, InlinePresentation inlineTooltipPresentation, RemoteViews dialogPresentation, RemoteViews dialogHeader, RemoteViews header, RemoteViews footer, IntentSender authentication, AutofillId[] authenticationIds, AutofillId[] ignoredIds, AutofillId[] fillDialogTriggerIds, long disableDuration, AutofillId[] fieldClassificationIds, int flags, int requestId, UserData userData, int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId, int serviceDisplayNameResourceId, boolean showFillDialogIcon, boolean showSaveDialogIcon, FieldClassification[] detectedFieldTypes, PendingIntent dialogPendingIntent)166     private FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState,
167             RemoteViews presentation, InlinePresentation inlinePresentation,
168             InlinePresentation inlineTooltipPresentation, RemoteViews dialogPresentation,
169             RemoteViews dialogHeader, RemoteViews header, RemoteViews footer,
170             IntentSender authentication, AutofillId[] authenticationIds, AutofillId[] ignoredIds,
171             AutofillId[] fillDialogTriggerIds, long disableDuration,
172             AutofillId[] fieldClassificationIds, int flags, int requestId, UserData userData,
173             int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId,
174             int serviceDisplayNameResourceId, boolean showFillDialogIcon,
175             boolean showSaveDialogIcon,
176             FieldClassification[] detectedFieldTypes, PendingIntent dialogPendingIntent) {
177         mDatasets = datasets;
178         mSaveInfo = saveInfo;
179         mClientState = clientState;
180         mPresentation = presentation;
181         mInlinePresentation = inlinePresentation;
182         mInlineTooltipPresentation = inlineTooltipPresentation;
183         mDialogPresentation = dialogPresentation;
184         mDialogHeader = dialogHeader;
185         mHeader = header;
186         mFooter = footer;
187         mAuthentication = authentication;
188         mAuthenticationIds = authenticationIds;
189         mIgnoredIds = ignoredIds;
190         mFillDialogTriggerIds = fillDialogTriggerIds;
191         mDisableDuration = disableDuration;
192         mFieldClassificationIds = fieldClassificationIds;
193         mFlags = flags;
194         mRequestId = requestId;
195         mUserData = userData;
196         mCancelIds = cancelIds;
197         mSupportsInlineSuggestions = supportsInlineSuggestions;
198         mIconResourceId = iconResourceId;
199         mServiceDisplayNameResourceId = serviceDisplayNameResourceId;
200         mShowFillDialogIcon = showFillDialogIcon;
201         mShowSaveDialogIcon = showSaveDialogIcon;
202         mDetectedFieldTypes = detectedFieldTypes;
203         mDialogPendingIntent = dialogPendingIntent;
204     }
205 
FillResponse(@onNull Builder builder)206     private FillResponse(@NonNull Builder builder) {
207         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
208         mSaveInfo = builder.mSaveInfo;
209         mClientState = builder.mClientState;
210         mPresentation = builder.mPresentation;
211         mInlinePresentation = builder.mInlinePresentation;
212         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
213         mDialogPresentation = builder.mDialogPresentation;
214         mDialogHeader = builder.mDialogHeader;
215         mHeader = builder.mHeader;
216         mFooter = builder.mFooter;
217         mAuthentication = builder.mAuthentication;
218         mAuthenticationIds = builder.mAuthenticationIds;
219         mFillDialogTriggerIds = builder.mFillDialogTriggerIds;
220         mIgnoredIds = builder.mIgnoredIds;
221         mDisableDuration = builder.mDisableDuration;
222         mFieldClassificationIds = builder.mFieldClassificationIds;
223         mFlags = builder.mFlags;
224         mRequestId = INVALID_REQUEST_ID;
225         mUserData = builder.mUserData;
226         mCancelIds = builder.mCancelIds;
227         mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
228         mIconResourceId = builder.mIconResourceId;
229         mServiceDisplayNameResourceId = builder.mServiceDisplayNameResourceId;
230         mShowFillDialogIcon = builder.mShowFillDialogIcon;
231         mShowSaveDialogIcon = builder.mShowSaveDialogIcon;
232         mDetectedFieldTypes = builder.mDetectedFieldTypes;
233         mDialogPendingIntent = builder.mDialogPendingIntent;
234     }
235 
236     /** @hide */
237     @TestApi
238     @NonNull
getDetectedFieldClassifications()239     public Set<FieldClassification> getDetectedFieldClassifications() {
240         return Set.of(mDetectedFieldTypes);
241     }
242 
243     /** @hide */
getClientState()244     public @Nullable Bundle getClientState() {
245         return mClientState;
246     }
247 
248     /** @hide */
getDatasets()249     public @Nullable List<Dataset> getDatasets() {
250         return (mDatasets != null) ? mDatasets.getList() : null;
251     }
252 
253     /** @hide */
getSaveInfo()254     public @Nullable SaveInfo getSaveInfo() {
255         return mSaveInfo;
256     }
257 
258     /** @hide */
getPresentation()259     public @Nullable RemoteViews getPresentation() {
260         return mPresentation;
261     }
262 
263     /** @hide */
getInlinePresentation()264     public @Nullable InlinePresentation getInlinePresentation() {
265         return mInlinePresentation;
266     }
267 
268     /** @hide */
getInlineTooltipPresentation()269     public @Nullable InlinePresentation getInlineTooltipPresentation() {
270         return mInlineTooltipPresentation;
271     }
272 
273     /** @hide */
getDialogPresentation()274     public @Nullable RemoteViews getDialogPresentation() {
275         return mDialogPresentation;
276     }
277 
278     /** @hide */
getDialogHeader()279     public @Nullable RemoteViews getDialogHeader() {
280         return mDialogHeader;
281     }
282 
283     /** @hide */
getHeader()284     public @Nullable RemoteViews getHeader() {
285         return mHeader;
286     }
287 
288     /** @hide */
getFooter()289     public @Nullable RemoteViews getFooter() {
290         return mFooter;
291     }
292 
293     /** @hide */
getAuthentication()294     public @Nullable IntentSender getAuthentication() {
295         return mAuthentication;
296     }
297 
298     /** @hide */
getAuthenticationIds()299     public @Nullable AutofillId[] getAuthenticationIds() {
300         return mAuthenticationIds;
301     }
302 
303     /** @hide */
getFillDialogTriggerIds()304     public @Nullable AutofillId[] getFillDialogTriggerIds() {
305         return mFillDialogTriggerIds;
306     }
307 
308     /** @hide */
getIgnoredIds()309     public @Nullable AutofillId[] getIgnoredIds() {
310         return mIgnoredIds;
311     }
312 
313     /** @hide */
getDisableDuration()314     public long getDisableDuration() {
315         return mDisableDuration;
316     }
317 
318     /** @hide */
getFieldClassificationIds()319     public @Nullable AutofillId[] getFieldClassificationIds() {
320         return mFieldClassificationIds;
321     }
322 
323     /** @hide */
getUserData()324     public @Nullable UserData getUserData() {
325         return mUserData;
326     }
327 
328     /** @hide */
getIconResourceId()329     public @DrawableRes int getIconResourceId() {
330         return mIconResourceId;
331     }
332 
333     /** @hide */
getServiceDisplayNameResourceId()334     public @StringRes int getServiceDisplayNameResourceId() {
335         return mServiceDisplayNameResourceId;
336     }
337 
338     /** @hide */
getShowFillDialogIcon()339     public boolean getShowFillDialogIcon() {
340         return mShowFillDialogIcon;
341     }
342 
343     /** @hide */
getShowSaveDialogIcon()344     public boolean getShowSaveDialogIcon() {
345         return mShowSaveDialogIcon;
346     }
347 
348     /** @hide */
349     @TestApi
getFlags()350     public int getFlags() {
351         return mFlags;
352     }
353 
354     /**
355      * Associates a {@link FillResponse} to a request.
356      *
357      * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}.
358      *
359      * @param requestId The id of the request to associate the response to.
360      *
361      * @hide
362      */
setRequestId(int requestId)363     public void setRequestId(int requestId) {
364         mRequestId = requestId;
365     }
366 
367     /** @hide */
getRequestId()368     public int getRequestId() {
369         return mRequestId;
370     }
371 
372     /** @hide */
373     @Nullable
getCancelIds()374     public int[] getCancelIds() {
375         return mCancelIds;
376     }
377 
378     /** @hide */
supportsInlineSuggestions()379     public boolean supportsInlineSuggestions() {
380         return mSupportsInlineSuggestions;
381     }
382 
383     /**
384      * Builder for {@link FillResponse} objects. You must to provide at least
385      * one dataset or set an authentication intent with a presentation view.
386      */
387     public static final class Builder {
388         private ArrayList<Dataset> mDatasets;
389         private SaveInfo mSaveInfo;
390         private Bundle mClientState;
391         private RemoteViews mPresentation;
392         private InlinePresentation mInlinePresentation;
393         private InlinePresentation mInlineTooltipPresentation;
394         private RemoteViews mDialogPresentation;
395         private RemoteViews mDialogHeader;
396         private RemoteViews mHeader;
397         private RemoteViews mFooter;
398         private IntentSender mAuthentication;
399         private AutofillId[] mAuthenticationIds;
400         private AutofillId[] mIgnoredIds;
401         private long mDisableDuration;
402         private AutofillId[] mFieldClassificationIds;
403         private AutofillId[] mFillDialogTriggerIds;
404         private int mFlags;
405         private boolean mDestroyed;
406         private UserData mUserData;
407         private int[] mCancelIds;
408         private boolean mSupportsInlineSuggestions;
409         private int mIconResourceId;
410         private int mServiceDisplayNameResourceId;
411         private boolean mShowFillDialogIcon = true;
412         private boolean mShowSaveDialogIcon = true;
413         private FieldClassification[] mDetectedFieldTypes;
414         private PendingIntent mDialogPendingIntent;
415 
416         /**
417          * Adds a new {@link FieldClassification} to this response, to
418          * help the platform provide more accurate detection results.
419          *
420          * Call this when a field has been detected with a type.
421          *
422          * Altough similiarly named with {@link #setFieldClassificationIds},
423          * it provides a different functionality - setFieldClassificationIds should
424          * be used when a field is only suspected to be Autofillable.
425          * This method should be used when a field is certainly Autofillable
426          * with a certain type.
427          */
428         @NonNull
setDetectedFieldClassifications( @onNull Set<FieldClassification> fieldInfos)429         public Builder setDetectedFieldClassifications(
430                 @NonNull Set<FieldClassification> fieldInfos) {
431             throwIfDestroyed();
432             throwIfDisableAutofillCalled();
433             mDetectedFieldTypes = fieldInfos.toArray(new FieldClassification[0]);
434             return this;
435         }
436 
437         /**
438          * Triggers a custom UI before autofilling the screen with any data set in this
439          * response.
440          *
441          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
442          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
443          * for examples.
444          *
445          * <p>This is typically useful when a user interaction is required to unlock their
446          * data vault if you encrypt the data set labels and data set data. It is recommended
447          * to encrypt only the sensitive data and not the data set labels which would allow
448          * auth on the data set level leading to a better user experience. Note that if you
449          * use sensitive data as a label, for example an email address, then it should also
450          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
451          * {@link Activity} which implements your authentication flow. Also if you provide an auth
452          * intent you also need to specify the presentation view to be shown in the fill UI
453          * for the user to trigger your authentication flow.
454          *
455          * <p>When a user triggers autofill, the system launches the provided intent
456          * whose extras will have the
457          * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
458          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
459          * client state}. Once you complete your authentication flow you should set the
460          * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
461          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
462          * with the fully populated {@link FillResponse response} (or {@code null} if the screen
463          * cannot be autofilled).
464          *
465          * <p> <b>IMPORTANT</b>: Extras must be non-null on the intent being set for Android 12
466          * otherwise it will cause a crash. Do not use {@link Activity#setResult(int)}, instead use
467          * {@link Activity#setResult(int, Intent) with non-null extras. Consider setting {
468          * @link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} to null or use
469          * {@link Bundle#EMPTY} with {@link Intent#putExtras(Bundle)} on the intent when
470          * finishing activity to avoid crash). </p>
471          *
472          * <p>For example, if you provided an empty {@link FillResponse response} because the
473          * user's data was locked and marked that the response needs an authentication then
474          * in the response returned if authentication succeeds you need to provide all
475          * available data sets some of which may need to be further authenticated, for
476          * example a credit card whose CVV needs to be entered.
477          *
478          * <p>If you provide an authentication intent you must also provide a presentation
479          * which is used to visualize the response for triggering the authentication
480          * flow.
481          *
482          * <p><b>Note:</b> Do not make the provided pending intent
483          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
484          * platform needs to fill in the authentication arguments.
485          *
486          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
487          * or background color: Autofill on different platforms may have different themes.
488          *
489          * @param authentication Intent to an activity with your authentication flow.
490          * @param presentation The presentation to visualize the response.
491          * @param ids id of Views that when focused will display the authentication UI.
492          *
493          * @return This builder.
494          *
495          * @throws IllegalArgumentException if any of the following occurs:
496          * <ul>
497          *   <li>{@code ids} is {@code null}</li>
498          *   <li>{@code ids} is empty</li>
499          *   <li>{@code ids} contains a {@code null} element</li>
500          *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
501          *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
502          * </ul>
503          *
504          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
505          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
506          *
507          * @see android.app.PendingIntent#getIntentSender()
508          * @deprecated Use
509          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
510          * instead.
511          */
512         @Deprecated
513         @NonNull
setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation)514         public Builder setAuthentication(@NonNull AutofillId[] ids,
515                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
516             throwIfDestroyed();
517             throwIfDisableAutofillCalled();
518             if (mHeader != null || mFooter != null) {
519                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
520             }
521 
522             if (authentication == null ^ presentation == null) {
523                 throw new IllegalArgumentException("authentication and presentation"
524                         + " must be both non-null or null");
525             }
526             mAuthentication = authentication;
527             mPresentation = presentation;
528             mAuthenticationIds = assertValid(ids);
529             return this;
530         }
531 
532         /**
533          * Triggers a custom UI before autofilling the screen with any data set in this
534          * response.
535          *
536          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
537          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
538          * for examples.
539          *
540          * <p>This method is similar to
541          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts
542          * an {@link InlinePresentation} presentation which is required for authenticating through
543          * the inline autofill flow.
544          *
545          * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
546          * not work with {@link InlinePresentation}.</p>
547          *
548          * @param authentication Intent to an activity with your authentication flow.
549          * @param presentation The presentation to visualize the response.
550          * @param inlinePresentation The inlinePresentation to visualize the response inline.
551          * @param ids id of Views that when focused will display the authentication UI.
552          *
553          * @return This builder.
554          *
555          * @throws IllegalArgumentException if any of the following occurs:
556          * <ul>
557          *   <li>{@code ids} is {@code null}</li>
558          *   <li>{@code ids} is empty</li>
559          *   <li>{@code ids} contains a {@code null} element</li>
560          *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
561          *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
562          *   <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li>
563          *   <li>both {@code authentication} and {@code inlinePresentation} are
564          *   non-{@code null}</li>
565          * </ul>
566          *
567          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
568          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
569          *
570          * @see android.app.PendingIntent#getIntentSender()
571          * @deprecated Use
572          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
573          * instead.
574          */
575         @Deprecated
576         @NonNull
setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation)577         public Builder setAuthentication(@NonNull AutofillId[] ids,
578                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
579                 @Nullable InlinePresentation inlinePresentation) {
580             return setAuthentication(ids, authentication, presentation, inlinePresentation, null);
581         }
582 
583         /**
584          * Triggers a custom UI before autofilling the screen with any data set in this
585          * response.
586          *
587          * <p>This method like
588          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)}
589          * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip.
590          *
591          * @deprecated Use
592          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
593          * instead.
594          */
595         @Deprecated
596         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation)597         public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
598                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
599                 @Nullable InlinePresentation inlinePresentation,
600                 @Nullable InlinePresentation inlineTooltipPresentation) {
601             throwIfDestroyed();
602             throwIfDisableAutofillCalled();
603             return setAuthentication(ids, authentication, presentation,
604                     inlinePresentation, inlineTooltipPresentation, null);
605         }
606 
607         /**
608          * Triggers a custom UI before autofilling the screen with any data set in this
609          * response.
610          *
611          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
612          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
613          * for examples.
614          *
615          * <p>This is typically useful when a user interaction is required to unlock their
616          * data vault if you encrypt the data set labels and data set data. It is recommended
617          * to encrypt only the sensitive data and not the data set labels which would allow
618          * auth on the data set level leading to a better user experience. Note that if you
619          * use sensitive data as a label, for example an email address, then it should also
620          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
621          * {@link Activity} which implements your authentication flow. Also if you provide an auth
622          * intent you also need to specify the presentation view to be shown in the fill UI
623          * for the user to trigger your authentication flow.
624          *
625          * <p>When a user triggers autofill, the system launches the provided intent
626          * whose extras will have the
627          * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
628          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
629          * client state}. Once you complete your authentication flow you should set the
630          * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
631          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
632          * with the fully populated {@link FillResponse response} (or {@code null} if the screen
633          * cannot be autofilled).
634          *
635          * <p>For example, if you provided an empty {@link FillResponse response} because the
636          * user's data was locked and marked that the response needs an authentication then
637          * in the response returned if authentication succeeds you need to provide all
638          * available data sets some of which may need to be further authenticated, for
639          * example a credit card whose CVV needs to be entered.
640          *
641          * <p>If you provide an authentication intent you must also provide a presentation
642          * which is used to visualize the response for triggering the authentication
643          * flow.
644          *
645          * <p><b>Note:</b> Do not make the provided pending intent
646          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
647          * platform needs to fill in the authentication arguments.
648          *
649          * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
650          * not work with {@link InlinePresentation}.</p>
651          *
652          * @param ids id of Views that when focused will display the authentication UI.
653          * @param authentication Intent to an activity with your authentication flow.
654          * @param presentations The presentations to visualize the response.
655          *
656          * @throws IllegalArgumentException if any of the following occurs:
657          * <ul>
658          *   <li>{@code ids} is {@code null}</li>
659          *   <li>{@code ids} is empty</li>
660          *   <li>{@code ids} contains a {@code null} element</li>
661          *   <li>{@code authentication} is {@code null}, but either or both of
662          *   {@code presentations.getPresentation()} and
663          *   {@code presentations.getInlinePresentation()} is non-{@code null}</li>
664          *   <li>{@code authentication} is non-{{@code null}, but both
665          *   {@code presentations.getPresentation()} and
666          *   {@code presentations.getInlinePresentation()} are {@code null}</li>
667          * </ul>
668          *
669          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
670          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
671          *
672          * @return This builder.
673          */
674         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable Presentations presentations)675         public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
676                 @Nullable IntentSender authentication,
677                 @Nullable Presentations presentations) {
678             throwIfDestroyed();
679             throwIfDisableAutofillCalled();
680             if (presentations == null) {
681                 return setAuthentication(ids, authentication, null, null, null, null);
682             }
683             return setAuthentication(ids, authentication,
684                     presentations.getMenuPresentation(),
685                     presentations.getInlinePresentation(),
686                     presentations.getInlineTooltipPresentation(),
687                     presentations.getDialogPresentation());
688         }
689 
690         /**
691          * Triggers a custom UI before autofilling the screen with any data set in this
692          * response.
693          */
694         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation, @Nullable RemoteViews dialogPresentation)695         private Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
696                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
697                 @Nullable InlinePresentation inlinePresentation,
698                 @Nullable InlinePresentation inlineTooltipPresentation,
699                 @Nullable RemoteViews dialogPresentation) {
700             throwIfDestroyed();
701             throwIfDisableAutofillCalled();
702             if (mHeader != null || mFooter != null) {
703                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
704             }
705 
706             if (authentication == null ^ (presentation == null && inlinePresentation == null)) {
707                 throw new IllegalArgumentException("authentication and presentation "
708                         + "(dropdown or inline), must be both non-null or null");
709             }
710             mAuthentication = authentication;
711             mPresentation = presentation;
712             mInlinePresentation = inlinePresentation;
713             mInlineTooltipPresentation = inlineTooltipPresentation;
714             mDialogPresentation = dialogPresentation;
715             mAuthenticationIds = assertValid(ids);
716             return this;
717         }
718 
719         /**
720          * Specifies views that should not trigger new
721          * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
722          * FillCallback)} requests.
723          *
724          * <p>This is typically used when the service cannot autofill the view; for example, a
725          * text field representing the result of a Captcha challenge.
726          */
727         @NonNull
setIgnoredIds(AutofillId...ids)728         public Builder setIgnoredIds(AutofillId...ids) {
729             throwIfDestroyed();
730             mIgnoredIds = ids;
731             return this;
732         }
733 
734         /**
735          * Adds a new {@link Dataset} to this response.
736          *
737          * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of
738          * datasets is limited by the Binder transaction size, so it's recommended to keep it
739          * small (in the range of 10-20 at most) and use pagination by adding a fake
740          * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end
741          * with a presentation string like "Next 10" that would return a new {@link FillResponse}
742          * with the next 10 datasets, and so on. This limitation was lifted on
743          * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction
744          * size can still be reached if each dataset itself is too big.
745          *
746          * @return This builder.
747          */
748         @NonNull
addDataset(@ullable Dataset dataset)749         public Builder addDataset(@Nullable Dataset dataset) {
750             throwIfDestroyed();
751             throwIfDisableAutofillCalled();
752             if (dataset == null) {
753                 return this;
754             }
755             if (mDatasets == null) {
756                 mDatasets = new ArrayList<>();
757             }
758             if (!mDatasets.add(dataset)) {
759                 return this;
760             }
761             return this;
762         }
763 
764         /**
765          * @hide
766          */
767         @NonNull
setDatasets(ArrayList<Dataset> dataset)768         public Builder setDatasets(ArrayList<Dataset> dataset) {
769             mDatasets = dataset;
770             return this;
771         }
772 
773         /**
774          * Sets the {@link SaveInfo} associated with this response.
775          *
776          * @return This builder.
777          */
setSaveInfo(@onNull SaveInfo saveInfo)778         public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
779             throwIfDestroyed();
780             throwIfDisableAutofillCalled();
781             mSaveInfo = saveInfo;
782             return this;
783         }
784 
785         /**
786          * Sets a bundle with state that is passed to subsequent APIs that manipulate this response.
787          *
788          * <p>You can use this bundle to store intermediate state that is passed to subsequent calls
789          * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
790          * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and
791          * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}.
792          *
793          * <p>If this method is called on multiple {@link FillResponse} objects for the same
794          * screen, just the latest bundle is passed back to the service.
795          *
796          * @param clientState The custom client state.
797          * @return This builder.
798          */
799         @NonNull
setClientState(@ullable Bundle clientState)800         public Builder setClientState(@Nullable Bundle clientState) {
801             throwIfDestroyed();
802             throwIfDisableAutofillCalled();
803             mClientState = clientState;
804             return this;
805         }
806 
807         /**
808          * Sets which fields are used for
809          * <a href="AutofillService.html#FieldClassification">field classification</a>
810          *
811          * <p><b>Note:</b> This method automatically adds the
812          * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
813 
814          * @throws IllegalArgumentException is length of {@code ids} args is more than
815          * {@link UserData#getMaxFieldClassificationIdsSize()}.
816          * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
817          * already called.
818          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
819          */
820         @NonNull
setFieldClassificationIds(@onNull AutofillId... ids)821         public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
822             throwIfDestroyed();
823             throwIfDisableAutofillCalled();
824             Preconditions.checkArrayElementsNotNull(ids, "ids");
825             Preconditions.checkArgumentInRange(ids.length, 1,
826                     UserData.getMaxFieldClassificationIdsSize(), "ids length");
827             mFieldClassificationIds = ids;
828             mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
829             return this;
830         }
831 
832         /**
833          * Sets flags changing the response behavior.
834          *
835          * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
836          * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}.
837          *
838          * @return This builder.
839          */
840         @NonNull
setFlags(@illResponseFlags int flags)841         public Builder setFlags(@FillResponseFlags int flags) {
842             throwIfDestroyed();
843             mFlags = Preconditions.checkFlagsArgument(flags,
844                     FLAG_TRACK_CONTEXT_COMMITED
845                             | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL
846                             | FLAG_CREDENTIAL_MANAGER_RESPONSE);
847             return this;
848         }
849 
850         /**
851          * Disables autofill for the app or activity.
852          *
853          * <p>This method is useful to optimize performance in cases where the service knows it
854          * can not autofill an app&mdash;for example, when the service has a list of "denylisted"
855          * apps such as office suites.
856          *
857          * <p>By default, it disables autofill for all activities in the app, unless the response is
858          * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}.
859          *
860          * <p>Autofill for the app or activity is automatically re-enabled after any of the
861          * following conditions:
862          *
863          * <ol>
864          *   <li>{@code duration} milliseconds have passed.
865          *   <li>The autofill service for the user has changed.
866          *   <li>The device has rebooted.
867          * </ol>
868          *
869          * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain
870          * disabled for autofill until they finish and restart.
871          *
872          * @param duration duration to disable autofill, in milliseconds.
873          *
874          * @return this builder
875          *
876          * @throws IllegalArgumentException if {@code duration} is not a positive number.
877          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
878          *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
879          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
880          *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
881          */
882         @NonNull
disableAutofill(long duration)883         public Builder disableAutofill(long duration) {
884             throwIfDestroyed();
885             if (duration <= 0) {
886                 throw new IllegalArgumentException("duration must be greater than 0");
887             }
888             if (mAuthentication != null || mDatasets != null || mSaveInfo != null
889                     || mFieldClassificationIds != null || mClientState != null) {
890                 throw new IllegalStateException("disableAutofill() must be the only method called");
891             }
892 
893             mDisableDuration = duration;
894             return this;
895         }
896 
897         /**
898          * Overwrites Save/Fill dialog header icon with a specific one specified by resource id.
899          * The image is pulled from the package, so id should be defined in the manifest.
900          *
901          * @param id {@link android.graphics.drawable.Drawable} resource id of the icon to be used.
902          * A value of 0 indicates to use the default header icon.
903          *
904          * @return this builder
905          */
906         @NonNull
setIconResourceId(@rawableRes int id)907         public Builder setIconResourceId(@DrawableRes int id) {
908             throwIfDestroyed();
909 
910             mIconResourceId = id;
911             return this;
912         }
913 
914         /**
915          * Overrides the service name in the Save Dialog header with a specific string defined
916          * in the service provider's manifest.xml
917          *
918          * @param id Resoure Id of the custom string defined in the provider's manifest. If set
919          * to 0, the default name will be used.
920          *
921          * @return this builder
922          */
923         @NonNull
setServiceDisplayNameResourceId(@tringRes int id)924         public Builder setServiceDisplayNameResourceId(@StringRes int id) {
925             throwIfDestroyed();
926 
927             mServiceDisplayNameResourceId = id;
928             return this;
929         }
930 
931         /**
932          * Whether or not to show the Autofill provider icon inside of the Fill Dialog
933          *
934          * @param show True to show, false to hide. Defaults to true.
935          *
936          * @return this builder
937          */
938         @NonNull
setShowFillDialogIcon(boolean show)939         public Builder setShowFillDialogIcon(boolean show) {
940             throwIfDestroyed();
941 
942             mShowFillDialogIcon = show;
943             return this;
944         }
945 
946         /**
947          * Whether or not to show the Autofill provider icon inside of the Save Dialog
948          *
949          * @param show True to show, false to hide. Defaults to true.
950          *
951          * @return this builder
952          */
953         @NonNull
setShowSaveDialogIcon(boolean show)954         public Builder setShowSaveDialogIcon(boolean show) {
955             throwIfDestroyed();
956 
957             mShowSaveDialogIcon = show;
958             return this;
959         }
960 
961         /**
962          * Sets a header to be shown as the first element in the list of datasets.
963          *
964          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
965          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
966          * method should only be used on {@link FillResponse FillResponses} that do not require
967          * authentication (as the header could have been set directly in the main presentation in
968          * these cases).
969          *
970          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
971          * or background color: Autofill on different platforms may have different themes.
972          *
973          * @param header a presentation to represent the header. This presentation is not clickable
974          * &mdash;calling
975          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
976          * have no effect.
977          *
978          * @return this builder
979          *
980          * @throws IllegalStateException if an
981          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
982          * authentication} was already set for this builder.
983          */
984         // TODO(b/69796626): make it sticky / update javadoc
985         @NonNull
setHeader(@onNull RemoteViews header)986         public Builder setHeader(@NonNull RemoteViews header) {
987             throwIfDestroyed();
988             throwIfAuthenticationCalled();
989             mHeader = Objects.requireNonNull(header);
990             return this;
991         }
992 
993         /**
994          * Sets a footer to be shown as the last element in the list of datasets.
995          *
996          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
997          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
998          * method should only be used on {@link FillResponse FillResponses} that do not require
999          * authentication (as the footer could have been set directly in the main presentation in
1000          * these cases).
1001          *
1002          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
1003          * or background color: Autofill on different platforms may have different themes.
1004          *
1005          * @param footer a presentation to represent the footer. This presentation is not clickable
1006          * &mdash;calling
1007          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
1008          * have no effect.
1009          *
1010          * @return this builder
1011          *
1012          * @throws IllegalStateException if the FillResponse
1013          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
1014          * requires authentication}.
1015          */
1016         // TODO(b/69796626): make it sticky / update javadoc
1017         @NonNull
setFooter(@onNull RemoteViews footer)1018         public Builder setFooter(@NonNull RemoteViews footer) {
1019             throwIfDestroyed();
1020             throwIfAuthenticationCalled();
1021             mFooter = Objects.requireNonNull(footer);
1022             return this;
1023         }
1024 
1025         /**
1026          * Sets a specific {@link UserData} for field classification for this request only.
1027          *
1028          * <p>Any fields in this UserData will override corresponding fields in the generic
1029          * UserData object
1030          *
1031          * @return this builder
1032          * @throws IllegalStateException if the FillResponse
1033          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
1034          * requires authentication}.
1035          */
1036         @NonNull
setUserData(@onNull UserData userData)1037         public Builder setUserData(@NonNull UserData userData) {
1038             throwIfDestroyed();
1039             throwIfAuthenticationCalled();
1040             mUserData = Objects.requireNonNull(userData);
1041             return this;
1042         }
1043 
1044         /**
1045          * Sets target resource IDs of the child view in {@link RemoteViews Presentation Template}
1046          * which will cancel the session when clicked.
1047          * Those targets will be respectively applied to a child of the header, footer and
1048          * each {@link Dataset}.
1049          *
1050          * @param ids array of the resource id. Empty list or non-existing id has no effect.
1051          *
1052          * @return this builder
1053          *
1054          * @throws IllegalStateException if {@link #build()} was already called.
1055          */
1056         @NonNull
setPresentationCancelIds(@ullable int[] ids)1057         public Builder setPresentationCancelIds(@Nullable int[] ids) {
1058             throwIfDestroyed();
1059             mCancelIds = ids;
1060             return this;
1061         }
1062 
1063         /**
1064          * Sets the presentation of header in fill dialog UI. The header should have
1065          * a prompt for what datasets are shown in the dialog. If this is not set,
1066          * the dialog only shows your application icon.
1067          *
1068          * More details about the fill dialog, see
1069          * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
1070          */
1071         @NonNull
setDialogHeader(@onNull RemoteViews header)1072         public Builder setDialogHeader(@NonNull RemoteViews header) {
1073             throwIfDestroyed();
1074             Objects.requireNonNull(header);
1075             mDialogHeader = header;
1076             return this;
1077         }
1078 
1079         /**
1080          * Sets which fields are used for the fill dialog UI.
1081          *
1082          * More details about the fill dialog, see
1083          * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
1084          *
1085          * @throws IllegalStateException if {@link #build()} was already called.
1086          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
1087          */
1088         @NonNull
setFillDialogTriggerIds(@onNull AutofillId... ids)1089         public Builder setFillDialogTriggerIds(@NonNull AutofillId... ids) {
1090             throwIfDestroyed();
1091             Preconditions.checkArrayElementsNotNull(ids, "ids");
1092             mFillDialogTriggerIds = ids;
1093             return this;
1094         }
1095 
1096         /**
1097          * Sets credential dialog pending intent. Framework will use the intent to launch the
1098          * selector UI. A replacement for previous fill bottom sheet.
1099          *
1100          * @throws IllegalStateException if {@link #build()} was already called.
1101          * @throws NullPointerException if {@code pendingIntent} is {@code null}.
1102          *
1103          * @hide
1104          */
1105         @NonNull
setDialogPendingIntent(@onNull PendingIntent pendingIntent)1106         public Builder setDialogPendingIntent(@NonNull PendingIntent pendingIntent) {
1107             throwIfDestroyed();
1108             Preconditions.checkNotNull(pendingIntent,
1109                     "can't pass a null object to setDialogPendingIntent");
1110             mDialogPendingIntent = pendingIntent;
1111             return this;
1112         }
1113 
1114         /**
1115          * Builds a new {@link FillResponse} instance.
1116          *
1117          * @throws IllegalStateException if any of the following conditions occur:
1118          * <ol>
1119          *   <li>{@link #build()} was already called.
1120          *   <li>No call was made to {@link #addDataset(Dataset)},
1121          *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
1122          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
1123          *       {@link #setClientState(Bundle)},
1124          *       or {@link #setFieldClassificationIds(AutofillId...)}.
1125          *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
1126          *       without any previous calls to {@link #addDataset(Dataset)}.
1127          * </ol>
1128          *
1129          * @return A built response.
1130          */
1131         @NonNull
build()1132         public FillResponse build() {
1133             throwIfDestroyed();
1134             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
1135                     && mDisableDuration == 0 && mFieldClassificationIds == null
1136                     && mClientState == null) {
1137                 throw new IllegalStateException("need to provide: at least one DataSet, or a "
1138                         + "SaveInfo, or an authentication with a presentation, "
1139                         + "or a FieldsDetection, or a client state, or disable autofill");
1140             }
1141             if (mDatasets == null && (mHeader != null || mFooter != null)) {
1142                 throw new IllegalStateException(
1143                         "must add at least 1 dataset when using header or footer");
1144             }
1145 
1146             if (mDatasets != null) {
1147                 for (final Dataset dataset : mDatasets) {
1148                     if (dataset.getFieldInlinePresentation(0) != null) {
1149                         mSupportsInlineSuggestions = true;
1150                         break;
1151                     }
1152                 }
1153             } else if (mInlinePresentation != null) {
1154                 mSupportsInlineSuggestions = true;
1155             }
1156 
1157             mDestroyed = true;
1158             return new FillResponse(this);
1159         }
1160 
throwIfDestroyed()1161         private void throwIfDestroyed() {
1162             if (mDestroyed) {
1163                 throw new IllegalStateException("Already called #build()");
1164             }
1165         }
1166 
throwIfDisableAutofillCalled()1167         private void throwIfDisableAutofillCalled() {
1168             if (mDisableDuration > 0) {
1169                 throw new IllegalStateException("Already called #disableAutofill()");
1170             }
1171         }
1172 
throwIfAuthenticationCalled()1173         private void throwIfAuthenticationCalled() {
1174             if (mAuthentication != null) {
1175                 throw new IllegalStateException("Already called #setAuthentication()");
1176             }
1177         }
1178     }
1179 
1180     /////////////////////////////////////
1181     // Object "contract" methods. //
1182     /////////////////////////////////////
1183     @Override
toString()1184     public String toString() {
1185         if (!sDebug) return super.toString();
1186 
1187         // TODO: create a dump() method instead
1188         final StringBuilder builder = new StringBuilder(
1189                 "FillResponse : [mRequestId=" + mRequestId);
1190         if (mDatasets != null) {
1191             builder.append(", datasets=").append(mDatasets.getList());
1192         }
1193         if (mSaveInfo != null) {
1194             builder.append(", saveInfo=").append(mSaveInfo);
1195         }
1196         if (mClientState != null) {
1197             builder.append(", hasClientState");
1198         }
1199         if (mPresentation != null) {
1200             builder.append(", hasPresentation");
1201         }
1202         if (mInlinePresentation != null) {
1203             builder.append(", hasInlinePresentation");
1204         }
1205         if (mInlineTooltipPresentation != null) {
1206             builder.append(", hasInlineTooltipPresentation");
1207         }
1208         if (mDialogPresentation != null) {
1209             builder.append(", hasDialogPresentation");
1210         }
1211         if (mDialogHeader != null) {
1212             builder.append(", hasDialogHeader");
1213         }
1214         if (mHeader != null) {
1215             builder.append(", hasHeader");
1216         }
1217         if (mFooter != null) {
1218             builder.append(", hasFooter");
1219         }
1220         if (mAuthentication != null) {
1221             builder.append(", hasAuthentication");
1222         }
1223         if (mDialogPendingIntent != null) {
1224             builder.append(", hasDialogPendingIntent");
1225         }
1226         if (mAuthenticationIds != null) {
1227             builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
1228         }
1229         if (mFillDialogTriggerIds != null) {
1230             builder.append(", fillDialogTriggerIds=")
1231                     .append(Arrays.toString(mFillDialogTriggerIds));
1232         }
1233         builder.append(", disableDuration=").append(mDisableDuration);
1234         if (mFlags != 0) {
1235             builder.append(", flags=").append(mFlags);
1236         }
1237         if (mFieldClassificationIds != null) {
1238             builder.append(Arrays.toString(mFieldClassificationIds));
1239         }
1240         if (mUserData != null) {
1241             builder.append(", userData=").append(mUserData);
1242         }
1243         if (mCancelIds != null) {
1244             builder.append(", mCancelIds=").append(mCancelIds.length);
1245         }
1246         builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions);
1247         return builder.append("]").toString();
1248     }
1249 
1250     /////////////////////////////////////
1251     // Parcelable "contract" methods. //
1252     /////////////////////////////////////
1253 
1254     @Override
describeContents()1255     public int describeContents() {
1256         return 0;
1257     }
1258 
1259     @Override
writeToParcel(Parcel parcel, int flags)1260     public void writeToParcel(Parcel parcel, int flags) {
1261         parcel.writeParcelable(mDatasets, flags);
1262         parcel.writeParcelable(mSaveInfo, flags);
1263         parcel.writeParcelable(mClientState, flags);
1264         parcel.writeParcelableArray(mAuthenticationIds, flags);
1265         parcel.writeParcelable(mAuthentication, flags);
1266         parcel.writeParcelable(mPresentation, flags);
1267         parcel.writeParcelable(mInlinePresentation, flags);
1268         parcel.writeParcelable(mInlineTooltipPresentation, flags);
1269         parcel.writeParcelable(mDialogPresentation, flags);
1270         parcel.writeParcelable(mDialogHeader, flags);
1271         parcel.writeParcelable(mDialogPendingIntent, flags);
1272         parcel.writeParcelableArray(mFillDialogTriggerIds, flags);
1273         parcel.writeParcelable(mHeader, flags);
1274         parcel.writeParcelable(mFooter, flags);
1275         parcel.writeParcelable(mUserData, flags);
1276         parcel.writeParcelableArray(mIgnoredIds, flags);
1277         parcel.writeLong(mDisableDuration);
1278         parcel.writeParcelableArray(mFieldClassificationIds, flags);
1279         parcel.writeParcelableArray(mDetectedFieldTypes, flags);
1280         parcel.writeInt(mIconResourceId);
1281         parcel.writeInt(mServiceDisplayNameResourceId);
1282         parcel.writeBoolean(mShowFillDialogIcon);
1283         parcel.writeBoolean(mShowSaveDialogIcon);
1284         parcel.writeInt(mFlags);
1285         parcel.writeIntArray(mCancelIds);
1286         parcel.writeInt(mRequestId);
1287     }
1288 
1289     public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
1290             new Parcelable.Creator<FillResponse>() {
1291         @Override
1292         public FillResponse createFromParcel(Parcel parcel) {
1293             // Always go through the builder to ensure the data ingested by
1294             // the system obeys the contract of the builder to avoid attacks
1295             // using specially crafted parcels.
1296             final Builder builder = new Builder();
1297             final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class);
1298             final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null;
1299             final int datasetCount = (datasets != null) ? datasets.size() : 0;
1300             for (int i = 0; i < datasetCount; i++) {
1301                 builder.addDataset(datasets.get(i));
1302             }
1303             builder.setSaveInfo(parcel.readParcelable(null, android.service.autofill.SaveInfo.class));
1304             builder.setClientState(parcel.readParcelable(null, android.os.Bundle.class));
1305 
1306             // Sets authentication state.
1307             final AutofillId[] authenticationIds = parcel.readParcelableArray(null,
1308                     AutofillId.class);
1309             final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
1310             final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
1311             final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
1312             final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
1313             final RemoteViews dialogPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
1314             if (authenticationIds != null) {
1315                 builder.setAuthentication(authenticationIds, authentication, presentation,
1316                         inlinePresentation, inlineTooltipPresentation, dialogPresentation);
1317             }
1318             final RemoteViews dialogHeader = parcel.readParcelable(null, android.widget.RemoteViews.class);
1319             if (dialogHeader != null) {
1320                 builder.setDialogHeader(dialogHeader);
1321             }
1322             final PendingIntent dialogPendingIntent = parcel.readParcelable(null,
1323                     PendingIntent.class);
1324             if (dialogPendingIntent != null) {
1325                 builder.setDialogPendingIntent(dialogPendingIntent);
1326             }
1327             final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class);
1328             if (triggerIds != null) {
1329                 builder.setFillDialogTriggerIds(triggerIds);
1330             }
1331             final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class);
1332             if (header != null) {
1333                 builder.setHeader(header);
1334             }
1335             final RemoteViews footer = parcel.readParcelable(null, android.widget.RemoteViews.class);
1336             if (footer != null) {
1337                 builder.setFooter(footer);
1338             }
1339             final UserData userData = parcel.readParcelable(null, android.service.autofill.UserData.class);
1340             if (userData != null) {
1341                 builder.setUserData(userData);
1342             }
1343 
1344             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
1345             final long disableDuration = parcel.readLong();
1346             if (disableDuration > 0) {
1347                 builder.disableAutofill(disableDuration);
1348             }
1349             final AutofillId[] fieldClassifactionIds =
1350                     parcel.readParcelableArray(null, AutofillId.class);
1351             if (fieldClassifactionIds != null) {
1352                 builder.setFieldClassificationIds(fieldClassifactionIds);
1353             }
1354 
1355             final FieldClassification[] detectedFields =
1356                     parcel.readParcelableArray(null, FieldClassification.class);
1357             if (detectedFields != null) {
1358                 builder.setDetectedFieldClassifications(Set.of(detectedFields));
1359             }
1360 
1361             builder.setIconResourceId(parcel.readInt());
1362             builder.setServiceDisplayNameResourceId(parcel.readInt());
1363             builder.setShowFillDialogIcon(parcel.readBoolean());
1364             builder.setShowSaveDialogIcon(parcel.readBoolean());
1365             builder.setFlags(parcel.readInt());
1366             final int[] cancelIds = parcel.createIntArray();
1367             builder.setPresentationCancelIds(cancelIds);
1368 
1369             final FillResponse response = builder.build();
1370             response.setRequestId(parcel.readInt());
1371 
1372             return response;
1373         }
1374 
1375         @Override
1376         public FillResponse[] newArray(int size) {
1377             return new FillResponse[size];
1378         }
1379     };
1380 }
1381