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