Home | History | Annotate | Download | only in autofill
      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 
     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 */
    105     public @Nullable Bundle getClientState() {
    106         return mClientState;
    107     }
    108 
    109     /** @hide */
    110     public @Nullable List<Dataset> getDatasets() {
    111         return (mDatasets != null) ? mDatasets.getList() : null;
    112     }
    113 
    114     /** @hide */
    115     public @Nullable SaveInfo getSaveInfo() {
    116         return mSaveInfo;
    117     }
    118 
    119     /** @hide */
    120     public @Nullable RemoteViews getPresentation() {
    121         return mPresentation;
    122     }
    123 
    124     /** @hide */
    125     public @Nullable RemoteViews getHeader() {
    126         return mHeader;
    127     }
    128 
    129     /** @hide */
    130     public @Nullable RemoteViews getFooter() {
    131         return mFooter;
    132     }
    133 
    134     /** @hide */
    135     public @Nullable IntentSender getAuthentication() {
    136         return mAuthentication;
    137     }
    138 
    139     /** @hide */
    140     public @Nullable AutofillId[] getAuthenticationIds() {
    141         return mAuthenticationIds;
    142     }
    143 
    144     /** @hide */
    145     public @Nullable AutofillId[] getIgnoredIds() {
    146         return mIgnoredIds;
    147     }
    148 
    149     /** @hide */
    150     public long getDisableDuration() {
    151         return mDisableDuration;
    152     }
    153 
    154     /** @hide */
    155     public @Nullable AutofillId[] getFieldClassificationIds() {
    156         return mFieldClassificationIds;
    157     }
    158 
    159     /** @hide */
    160     @TestApi
    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      */
    174     public void setRequestId(int requestId) {
    175         mRequestId = requestId;
    176     }
    177 
    178     /** @hide */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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
    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
    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          */
    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 
    542         private void throwIfDestroyed() {
    543             if (mDestroyed) {
    544                 throw new IllegalStateException("Already called #build()");
    545             }
    546         }
    547 
    548         private void throwIfDisableAutofillCalled() {
    549             if (mDisableDuration > 0) {
    550                 throw new IllegalStateException("Already called #disableAutofill()");
    551             }
    552         }
    553 
    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
    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
    610     public int describeContents() {
    611         return 0;
    612     }
    613 
    614     @Override
    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