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}—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)}— 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—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 * —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 * —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