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