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