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.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.TestApi;
27 import android.app.Activity;
28 import android.content.IntentSender;
29 import android.content.pm.ParceledListSlice;
30 import android.os.Bundle;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.view.autofill.AutofillId;
34 import android.widget.RemoteViews;
35 
36 import com.android.internal.util.Preconditions;
37 
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.List;
43 
44 /**
45  * Response for an {@link
46  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
47  *
48  * <p>See the main {@link AutofillService} documentation for more details and examples.
49  */
50 public final class FillResponse implements Parcelable {
51 
52     /**
53      * Flag used to generate {@link FillEventHistory.Event events} of type
54      * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}&mdash;if this flag is not passed to
55      * {@link Builder#setFlags(int)}, these events are not generated.
56      */
57     public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1;
58 
59     /**
60      * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}&mdash;
61      * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the
62      * activiy that generated the {@link FillRequest}, not the whole app.
63      */
64     public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
65 
66     /** @hide */
67     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
68             FLAG_TRACK_CONTEXT_COMMITED,
69             FLAG_DISABLE_ACTIVITY_ONLY
70     })
71     @Retention(RetentionPolicy.SOURCE)
72     @interface FillResponseFlags {}
73 
74     private final @Nullable ParceledListSlice<Dataset> mDatasets;
75     private final @Nullable SaveInfo mSaveInfo;
76     private final @Nullable Bundle mClientState;
77     private final @Nullable RemoteViews mPresentation;
78     private final @Nullable RemoteViews mHeader;
79     private final @Nullable RemoteViews mFooter;
80     private final @Nullable IntentSender mAuthentication;
81     private final @Nullable AutofillId[] mAuthenticationIds;
82     private final @Nullable AutofillId[] mIgnoredIds;
83     private final long mDisableDuration;
84     private final @Nullable AutofillId[] mFieldClassificationIds;
85     private final int mFlags;
86     private int mRequestId;
87     private final @Nullable UserData mUserData;
88 
FillResponse(@onNull Builder builder)89     private FillResponse(@NonNull Builder builder) {
90         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
91         mSaveInfo = builder.mSaveInfo;
92         mClientState = builder.mClientState;
93         mPresentation = builder.mPresentation;
94         mHeader = builder.mHeader;
95         mFooter = builder.mFooter;
96         mAuthentication = builder.mAuthentication;
97         mAuthenticationIds = builder.mAuthenticationIds;
98         mIgnoredIds = builder.mIgnoredIds;
99         mDisableDuration = builder.mDisableDuration;
100         mFieldClassificationIds = builder.mFieldClassificationIds;
101         mFlags = builder.mFlags;
102         mRequestId = INVALID_REQUEST_ID;
103         mUserData = builder.mUserData;
104     }
105 
106     /** @hide */
getClientState()107     public @Nullable Bundle getClientState() {
108         return mClientState;
109     }
110 
111     /** @hide */
getDatasets()112     public @Nullable List<Dataset> getDatasets() {
113         return (mDatasets != null) ? mDatasets.getList() : null;
114     }
115 
116     /** @hide */
getSaveInfo()117     public @Nullable SaveInfo getSaveInfo() {
118         return mSaveInfo;
119     }
120 
121     /** @hide */
getPresentation()122     public @Nullable RemoteViews getPresentation() {
123         return mPresentation;
124     }
125 
126     /** @hide */
getHeader()127     public @Nullable RemoteViews getHeader() {
128         return mHeader;
129     }
130 
131     /** @hide */
getFooter()132     public @Nullable RemoteViews getFooter() {
133         return mFooter;
134     }
135 
136     /** @hide */
getAuthentication()137     public @Nullable IntentSender getAuthentication() {
138         return mAuthentication;
139     }
140 
141     /** @hide */
getAuthenticationIds()142     public @Nullable AutofillId[] getAuthenticationIds() {
143         return mAuthenticationIds;
144     }
145 
146     /** @hide */
getIgnoredIds()147     public @Nullable AutofillId[] getIgnoredIds() {
148         return mIgnoredIds;
149     }
150 
151     /** @hide */
getDisableDuration()152     public long getDisableDuration() {
153         return mDisableDuration;
154     }
155 
156     /** @hide */
getFieldClassificationIds()157     public @Nullable AutofillId[] getFieldClassificationIds() {
158         return mFieldClassificationIds;
159     }
160 
161     /** @hide */
getUserData()162     public @Nullable UserData getUserData() {
163         return mUserData;
164     }
165 
166     /** @hide */
167     @TestApi
getFlags()168     public int getFlags() {
169         return mFlags;
170     }
171 
172     /**
173      * Associates a {@link FillResponse} to a request.
174      *
175      * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}.
176      *
177      * @param requestId The id of the request to associate the response to.
178      *
179      * @hide
180      */
setRequestId(int requestId)181     public void setRequestId(int requestId) {
182         mRequestId = requestId;
183     }
184 
185     /** @hide */
getRequestId()186     public int getRequestId() {
187         return mRequestId;
188     }
189 
190     /**
191      * Builder for {@link FillResponse} objects. You must to provide at least
192      * one dataset or set an authentication intent with a presentation view.
193      */
194     public static final class Builder {
195         private ArrayList<Dataset> mDatasets;
196         private SaveInfo mSaveInfo;
197         private Bundle mClientState;
198         private RemoteViews mPresentation;
199         private RemoteViews mHeader;
200         private RemoteViews mFooter;
201         private IntentSender mAuthentication;
202         private AutofillId[] mAuthenticationIds;
203         private AutofillId[] mIgnoredIds;
204         private long mDisableDuration;
205         private AutofillId[] mFieldClassificationIds;
206         private int mFlags;
207         private boolean mDestroyed;
208         private UserData mUserData;
209 
210         /**
211          * Triggers a custom UI before before autofilling the screen with any data set in this
212          * response.
213          *
214          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
215          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
216          * for examples.
217          *
218          * <p>This is typically useful when a user interaction is required to unlock their
219          * data vault if you encrypt the data set labels and data set data. It is recommended
220          * to encrypt only the sensitive data and not the data set labels which would allow
221          * auth on the data set level leading to a better user experience. Note that if you
222          * use sensitive data as a label, for example an email address, then it should also
223          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
224          * {@link Activity} which implements your authentication flow. Also if you provide an auth
225          * intent you also need to specify the presentation view to be shown in the fill UI
226          * for the user to trigger your authentication flow.
227          *
228          * <p>When a user triggers autofill, the system launches the provided intent
229          * whose extras will have the
230          * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
231          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
232          * client state}. Once you complete your authentication flow you should set the
233          * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
234          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
235          * with the fully populated {@link FillResponse response} (or {@code null} if the screen
236          * cannot be autofilled).
237          *
238          * <p>For example, if you provided an empty {@link FillResponse response} because the
239          * user's data was locked and marked that the response needs an authentication then
240          * in the response returned if authentication succeeds you need to provide all
241          * available data sets some of which may need to be further authenticated, for
242          * example a credit card whose CVV needs to be entered.
243          *
244          * <p>If you provide an authentication intent you must also provide a presentation
245          * which is used to visualize visualize the response for triggering the authentication
246          * flow.
247          *
248          * <p><b>Note:</b> Do not make the provided pending intent
249          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
250          * platform needs to fill in the authentication arguments.
251          *
252          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
253          * or background color: Autofill on different platforms may have different themes.
254          *
255          * @param authentication Intent to an activity with your authentication flow.
256          * @param presentation The presentation to visualize the response.
257          * @param ids id of Views that when focused will display the authentication UI.
258          *
259          * @return This builder.
260          *
261          * @throws IllegalArgumentException if any of the following occurs:
262          * <ul>
263          *   <li>{@code ids} is {@code null}</li>
264          *   <li>{@code ids} is empty</li>
265          *   <li>{@code ids} contains a {@code null} element</li>
266          *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
267          *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
268          * </ul>
269          *
270          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
271          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
272          *
273          * @see android.app.PendingIntent#getIntentSender()
274          */
275         @NonNull
setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation)276         public Builder setAuthentication(@NonNull AutofillId[] ids,
277                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
278             throwIfDestroyed();
279             throwIfDisableAutofillCalled();
280             if (mHeader != null || mFooter != null) {
281                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
282             }
283 
284             if (authentication == null ^ presentation == null) {
285                 throw new IllegalArgumentException("authentication and presentation"
286                         + " must be both non-null or null");
287             }
288             mAuthentication = authentication;
289             mPresentation = presentation;
290             mAuthenticationIds = assertValid(ids);
291             return this;
292         }
293 
294         /**
295          * Specifies views that should not trigger new
296          * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
297          * FillCallback)} requests.
298          *
299          * <p>This is typically used when the service cannot autofill the view; for example, a
300          * text field representing the result of a Captcha challenge.
301          */
302         @NonNull
setIgnoredIds(AutofillId...ids)303         public Builder setIgnoredIds(AutofillId...ids) {
304             throwIfDestroyed();
305             mIgnoredIds = ids;
306             return this;
307         }
308 
309         /**
310          * Adds a new {@link Dataset} to this response.
311          *
312          * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of
313          * datasets is limited by the Binder transaction size, so it's recommended to keep it
314          * small (in the range of 10-20 at most) and use pagination by adding a fake
315          * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end
316          * with a presentation string like "Next 10" that would return a new {@link FillResponse}
317          * with the next 10 datasets, and so on. This limitation was lifted on
318          * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction
319          * size can still be reached if each dataset itself is too big.
320          *
321          * @return This builder.
322          */
323         @NonNull
addDataset(@ullable Dataset dataset)324         public Builder addDataset(@Nullable Dataset dataset) {
325             throwIfDestroyed();
326             throwIfDisableAutofillCalled();
327             if (dataset == null) {
328                 return this;
329             }
330             if (mDatasets == null) {
331                 mDatasets = new ArrayList<>();
332             }
333             if (!mDatasets.add(dataset)) {
334                 return this;
335             }
336             return this;
337         }
338 
339         /**
340          * Sets the {@link SaveInfo} associated with this response.
341          *
342          * @return This builder.
343          */
setSaveInfo(@onNull SaveInfo saveInfo)344         public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
345             throwIfDestroyed();
346             throwIfDisableAutofillCalled();
347             mSaveInfo = saveInfo;
348             return this;
349         }
350 
351         /**
352          * Sets a bundle with state that is passed to subsequent APIs that manipulate this response.
353          *
354          * <p>You can use this bundle to store intermediate state that is passed to subsequent calls
355          * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
356          * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and
357          * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}.
358          *
359          * <p>If this method is called on multiple {@link FillResponse} objects for the same
360          * screen, just the latest bundle is passed back to the service.
361          *
362          * @param clientState The custom client state.
363          * @return This builder.
364          */
365         @NonNull
setClientState(@ullable Bundle clientState)366         public Builder setClientState(@Nullable Bundle clientState) {
367             throwIfDestroyed();
368             throwIfDisableAutofillCalled();
369             mClientState = clientState;
370             return this;
371         }
372 
373         /**
374          * Sets which fields are used for
375          * <a href="AutofillService.html#FieldClassification">field classification</a>
376          *
377          * <p><b>Note:</b> This method automatically adds the
378          * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
379 
380          * @throws IllegalArgumentException is length of {@code ids} args is more than
381          * {@link UserData#getMaxFieldClassificationIdsSize()}.
382          * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
383          * already called.
384          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
385          */
386         @NonNull
setFieldClassificationIds(@onNull AutofillId... ids)387         public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
388             throwIfDestroyed();
389             throwIfDisableAutofillCalled();
390             Preconditions.checkArrayElementsNotNull(ids, "ids");
391             Preconditions.checkArgumentInRange(ids.length, 1,
392                     UserData.getMaxFieldClassificationIdsSize(), "ids length");
393             mFieldClassificationIds = ids;
394             mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
395             return this;
396         }
397 
398         /**
399          * Sets flags changing the response behavior.
400          *
401          * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
402          * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}.
403          *
404          * @return This builder.
405          */
406         @NonNull
setFlags(@illResponseFlags int flags)407         public Builder setFlags(@FillResponseFlags int flags) {
408             throwIfDestroyed();
409             mFlags = Preconditions.checkFlagsArgument(flags,
410                     FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
411             return this;
412         }
413 
414         /**
415          * Disables autofill for the app or activity.
416          *
417          * <p>This method is useful to optimize performance in cases where the service knows it
418          * can not autofill an app&mdash;for example, when the service has a list of "blacklisted"
419          * apps such as office suites.
420          *
421          * <p>By default, it disables autofill for all activities in the app, unless the response is
422          * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}.
423          *
424          * <p>Autofill for the app or activity is automatically re-enabled after any of the
425          * following conditions:
426          *
427          * <ol>
428          *   <li>{@code duration} milliseconds have passed.
429          *   <li>The autofill service for the user has changed.
430          *   <li>The device has rebooted.
431          * </ol>
432          *
433          * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain
434          * disabled for autofill until they finish and restart.
435          *
436          * @param duration duration to disable autofill, in milliseconds.
437          *
438          * @return this builder
439          *
440          * @throws IllegalArgumentException if {@code duration} is not a positive number.
441          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
442          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
443          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
444          *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
445          */
446         @NonNull
disableAutofill(long duration)447         public Builder disableAutofill(long duration) {
448             throwIfDestroyed();
449             if (duration <= 0) {
450                 throw new IllegalArgumentException("duration must be greater than 0");
451             }
452             if (mAuthentication != null || mDatasets != null || mSaveInfo != null
453                     || mFieldClassificationIds != null || mClientState != null) {
454                 throw new IllegalStateException("disableAutofill() must be the only method called");
455             }
456 
457             mDisableDuration = duration;
458             return this;
459         }
460 
461         /**
462          * Sets a header to be shown as the first element in the list of datasets.
463          *
464          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
465          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
466          * method should only be used on {@link FillResponse FillResponses} that do not require
467          * authentication (as the header could have been set directly in the main presentation in
468          * these cases).
469          *
470          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
471          * or background color: Autofill on different platforms may have different themes.
472          *
473          * @param header a presentation to represent the header. This presentation is not clickable
474          * &mdash;calling
475          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
476          * have no effect.
477          *
478          * @return this builder
479          *
480          * @throws IllegalStateException if an
481          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was
482          * already set for this builder.
483          */
484         // TODO(b/69796626): make it sticky / update javadoc
485         @NonNull
setHeader(@onNull RemoteViews header)486         public Builder setHeader(@NonNull RemoteViews header) {
487             throwIfDestroyed();
488             throwIfAuthenticationCalled();
489             mHeader = Preconditions.checkNotNull(header);
490             return this;
491         }
492 
493         /**
494          * Sets a footer to be shown as the last element in the list of datasets.
495          *
496          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
497          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
498          * method should only be used on {@link FillResponse FillResponses} that do not require
499          * authentication (as the footer could have been set directly in the main presentation in
500          * these cases).
501          *
502          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
503          * or background color: Autofill on different platforms may have different themes.
504          *
505          * @param footer a presentation to represent the footer. This presentation is not clickable
506          * &mdash;calling
507          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
508          * have no effect.
509          *
510          * @return this builder
511          *
512          * @throws IllegalStateException if the FillResponse
513          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
514          * requires authentication}.
515          */
516         // TODO(b/69796626): make it sticky / update javadoc
517         @NonNull
setFooter(@onNull RemoteViews footer)518         public Builder setFooter(@NonNull RemoteViews footer) {
519             throwIfDestroyed();
520             throwIfAuthenticationCalled();
521             mFooter = Preconditions.checkNotNull(footer);
522             return this;
523         }
524 
525         /**
526          * Sets a specific {@link UserData} for field classification for this request only.
527          *
528          * <p>Any fields in this UserData will override corresponding fields in the generic
529          * UserData object
530          *
531          * @return this builder
532          * @throws IllegalStateException if the FillResponse
533          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
534          * requires authentication}.
535          */
536         @NonNull
setUserData(@onNull UserData userData)537         public Builder setUserData(@NonNull UserData userData) {
538             throwIfDestroyed();
539             throwIfAuthenticationCalled();
540             mUserData = Preconditions.checkNotNull(userData);
541             return this;
542         }
543 
544         /**
545          * Builds a new {@link FillResponse} instance.
546          *
547          * @throws IllegalStateException if any of the following conditions occur:
548          * <ol>
549          *   <li>{@link #build()} was already called.
550          *   <li>No call was made to {@link #addDataset(Dataset)},
551          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
552          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
553          *       {@link #setClientState(Bundle)},
554          *       or {@link #setFieldClassificationIds(AutofillId...)}.
555          *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
556          *       without any previous calls to {@link #addDataset(Dataset)}.
557          * </ol>
558          *
559          * @return A built response.
560          */
561         @NonNull
build()562         public FillResponse build() {
563             throwIfDestroyed();
564             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
565                     && mDisableDuration == 0 && mFieldClassificationIds == null
566                     && mClientState == null) {
567                 throw new IllegalStateException("need to provide: at least one DataSet, or a "
568                         + "SaveInfo, or an authentication with a presentation, "
569                         + "or a FieldsDetection, or a client state, or disable autofill");
570             }
571             if (mDatasets == null && (mHeader != null || mFooter != null)) {
572                 throw new IllegalStateException(
573                         "must add at least 1 dataset when using header or footer");
574             }
575             mDestroyed = true;
576             return new FillResponse(this);
577         }
578 
throwIfDestroyed()579         private void throwIfDestroyed() {
580             if (mDestroyed) {
581                 throw new IllegalStateException("Already called #build()");
582             }
583         }
584 
throwIfDisableAutofillCalled()585         private void throwIfDisableAutofillCalled() {
586             if (mDisableDuration > 0) {
587                 throw new IllegalStateException("Already called #disableAutofill()");
588             }
589         }
590 
throwIfAuthenticationCalled()591         private void throwIfAuthenticationCalled() {
592             if (mAuthentication != null) {
593                 throw new IllegalStateException("Already called #setAuthentication()");
594             }
595         }
596     }
597 
598     /////////////////////////////////////
599     // Object "contract" methods. //
600     /////////////////////////////////////
601     @Override
toString()602     public String toString() {
603         if (!sDebug) return super.toString();
604 
605         // TODO: create a dump() method instead
606         final StringBuilder builder = new StringBuilder(
607                 "FillResponse : [mRequestId=" + mRequestId);
608         if (mDatasets != null) {
609             builder.append(", datasets=").append(mDatasets.getList());
610         }
611         if (mSaveInfo != null) {
612             builder.append(", saveInfo=").append(mSaveInfo);
613         }
614         if (mClientState != null) {
615             builder.append(", hasClientState");
616         }
617         if (mPresentation != null) {
618             builder.append(", hasPresentation");
619         }
620         if (mHeader != null) {
621             builder.append(", hasHeader");
622         }
623         if (mFooter != null) {
624             builder.append(", hasFooter");
625         }
626         if (mAuthentication != null) {
627             builder.append(", hasAuthentication");
628         }
629         if (mAuthenticationIds != null) {
630             builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
631         }
632         builder.append(", disableDuration=").append(mDisableDuration);
633         if (mFlags != 0) {
634             builder.append(", flags=").append(mFlags);
635         }
636         if (mFieldClassificationIds != null) {
637             builder.append(Arrays.toString(mFieldClassificationIds));
638         }
639         if (mUserData != null) {
640             builder.append(", userData=").append(mUserData);
641         }
642         return builder.append("]").toString();
643     }
644 
645     /////////////////////////////////////
646     // Parcelable "contract" methods. //
647     /////////////////////////////////////
648 
649     @Override
describeContents()650     public int describeContents() {
651         return 0;
652     }
653 
654     @Override
writeToParcel(Parcel parcel, int flags)655     public void writeToParcel(Parcel parcel, int flags) {
656         parcel.writeParcelable(mDatasets, flags);
657         parcel.writeParcelable(mSaveInfo, flags);
658         parcel.writeParcelable(mClientState, flags);
659         parcel.writeParcelableArray(mAuthenticationIds, flags);
660         parcel.writeParcelable(mAuthentication, flags);
661         parcel.writeParcelable(mPresentation, flags);
662         parcel.writeParcelable(mHeader, flags);
663         parcel.writeParcelable(mFooter, flags);
664         parcel.writeParcelable(mUserData, flags);
665         parcel.writeParcelableArray(mIgnoredIds, flags);
666         parcel.writeLong(mDisableDuration);
667         parcel.writeParcelableArray(mFieldClassificationIds, flags);
668         parcel.writeInt(mFlags);
669         parcel.writeInt(mRequestId);
670     }
671 
672     public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
673             new Parcelable.Creator<FillResponse>() {
674         @Override
675         public FillResponse createFromParcel(Parcel parcel) {
676             // Always go through the builder to ensure the data ingested by
677             // the system obeys the contract of the builder to avoid attacks
678             // using specially crafted parcels.
679             final Builder builder = new Builder();
680             final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null);
681             final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null;
682             final int datasetCount = (datasets != null) ? datasets.size() : 0;
683             for (int i = 0; i < datasetCount; i++) {
684                 builder.addDataset(datasets.get(i));
685             }
686             builder.setSaveInfo(parcel.readParcelable(null));
687             builder.setClientState(parcel.readParcelable(null));
688 
689             // Sets authentication state.
690             final AutofillId[] authenticationIds = parcel.readParcelableArray(null,
691                     AutofillId.class);
692             final IntentSender authentication = parcel.readParcelable(null);
693             final RemoteViews presentation = parcel.readParcelable(null);
694             if (authenticationIds != null) {
695                 builder.setAuthentication(authenticationIds, authentication, presentation);
696             }
697             final RemoteViews header = parcel.readParcelable(null);
698             if (header != null) {
699                 builder.setHeader(header);
700             }
701             final RemoteViews footer = parcel.readParcelable(null);
702             if (footer != null) {
703                 builder.setFooter(footer);
704             }
705             final UserData userData = parcel.readParcelable(null);
706             if (userData != null) {
707                 builder.setUserData(userData);
708             }
709 
710             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
711             final long disableDuration = parcel.readLong();
712             if (disableDuration > 0) {
713                 builder.disableAutofill(disableDuration);
714             }
715             final AutofillId[] fieldClassifactionIds =
716                     parcel.readParcelableArray(null, AutofillId.class);
717             if (fieldClassifactionIds != null) {
718                 builder.setFieldClassificationIds(fieldClassifactionIds);
719             }
720             builder.setFlags(parcel.readInt());
721 
722             final FillResponse response = builder.build();
723             response.setRequestId(parcel.readInt());
724 
725             return response;
726         }
727 
728         @Override
729         public FillResponse[] newArray(int size) {
730             return new FillResponse[size];
731         }
732     };
733 }
734