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