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