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.DrawableRes; 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.StringRes; 28 import android.annotation.SuppressLint; 29 import android.annotation.TestApi; 30 import android.app.Activity; 31 import android.app.PendingIntent; 32 import android.content.Intent; 33 import android.content.IntentSender; 34 import android.content.pm.ParceledListSlice; 35 import android.os.Bundle; 36 import android.os.Parcel; 37 import android.os.Parcelable; 38 import android.service.assist.classification.FieldClassification; 39 import android.view.autofill.AutofillId; 40 import android.widget.RemoteViews; 41 42 import com.android.internal.util.Preconditions; 43 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.Objects; 50 import java.util.Set; 51 52 /** 53 * Response for an {@link 54 * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}. 55 * 56 * <p>See the main {@link AutofillService} documentation for more details and examples. 57 */ 58 public final class FillResponse implements Parcelable { 59 // common_typos_disable 60 61 /** 62 * Flag used to generate {@link FillEventHistory.Event events} of type 63 * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}—if this flag is not passed to 64 * {@link Builder#setFlags(int)}, these events are not generated. 65 */ 66 public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1; 67 68 /** 69 * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}— 70 * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the 71 * activiy that generated the {@link FillRequest}, not the whole app. 72 */ 73 public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2; 74 75 /** 76 * Flag used to request to wait for a delayed fill from the remote Autofill service if it's 77 * passed to {@link Builder#setFlags(int)}. 78 * 79 * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send 80 * a {@link FillResponse} to the latest {@link FillRequest} via 81 * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback} 82 * has timed out. 83 */ 84 public static final int FLAG_DELAY_FILL = 0x4; 85 86 /** 87 * @hide 88 */ 89 public static final int FLAG_CREDENTIAL_MANAGER_RESPONSE = 0x8; 90 91 /** @hide */ 92 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 93 FLAG_TRACK_CONTEXT_COMMITED, 94 FLAG_DISABLE_ACTIVITY_ONLY, 95 FLAG_DELAY_FILL, 96 FLAG_CREDENTIAL_MANAGER_RESPONSE 97 }) 98 @Retention(RetentionPolicy.SOURCE) 99 @interface FillResponseFlags {} 100 101 private final @Nullable ParceledListSlice<Dataset> mDatasets; 102 private final @Nullable SaveInfo mSaveInfo; 103 private final @Nullable Bundle mClientState; 104 private final @Nullable RemoteViews mPresentation; 105 private final @Nullable InlinePresentation mInlinePresentation; 106 private final @Nullable InlinePresentation mInlineTooltipPresentation; 107 private final @Nullable RemoteViews mDialogPresentation; 108 private final @Nullable RemoteViews mDialogHeader; 109 private final @Nullable RemoteViews mHeader; 110 private final @Nullable RemoteViews mFooter; 111 private final @Nullable IntentSender mAuthentication; 112 private final @Nullable AutofillId[] mAuthenticationIds; 113 private final @Nullable AutofillId[] mIgnoredIds; 114 private final @Nullable AutofillId[] mFillDialogTriggerIds; 115 private final long mDisableDuration; 116 private final @Nullable AutofillId[] mFieldClassificationIds; 117 private final int mFlags; 118 private int mRequestId; 119 private final @Nullable UserData mUserData; 120 private final @Nullable int[] mCancelIds; 121 private final boolean mSupportsInlineSuggestions; 122 private final @DrawableRes int mIconResourceId; 123 private final @StringRes int mServiceDisplayNameResourceId; 124 private final boolean mShowFillDialogIcon; 125 private final boolean mShowSaveDialogIcon; 126 private final @Nullable FieldClassification[] mDetectedFieldTypes; 127 private final @Nullable PendingIntent mDialogPendingIntent; 128 129 /** 130 * Creates a shollow copy of the provided FillResponse. 131 * 132 * @hide 133 */ shallowCopy( FillResponse r, List<Dataset> datasets, SaveInfo saveInfo)134 public static FillResponse shallowCopy( 135 FillResponse r, List<Dataset> datasets, SaveInfo saveInfo) { 136 return new FillResponse( 137 (datasets != null) ? new ParceledListSlice<>(datasets) : null, 138 saveInfo, 139 r.mClientState, 140 r.mPresentation, 141 r.mInlinePresentation, 142 r.mInlineTooltipPresentation, 143 r.mDialogPresentation, 144 r.mDialogHeader, 145 r.mHeader, 146 r.mFooter, 147 r.mAuthentication, 148 r.mAuthenticationIds, 149 r.mIgnoredIds, 150 r.mFillDialogTriggerIds, 151 r.mDisableDuration, 152 r.mFieldClassificationIds, 153 r.mFlags, 154 r.mRequestId, 155 r.mUserData, 156 r.mCancelIds, 157 r.mSupportsInlineSuggestions, 158 r.mIconResourceId, 159 r.mServiceDisplayNameResourceId, 160 r.mShowFillDialogIcon, 161 r.mShowSaveDialogIcon, 162 r.mDetectedFieldTypes, 163 r.mDialogPendingIntent); 164 } 165 FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState, RemoteViews presentation, InlinePresentation inlinePresentation, InlinePresentation inlineTooltipPresentation, RemoteViews dialogPresentation, RemoteViews dialogHeader, RemoteViews header, RemoteViews footer, IntentSender authentication, AutofillId[] authenticationIds, AutofillId[] ignoredIds, AutofillId[] fillDialogTriggerIds, long disableDuration, AutofillId[] fieldClassificationIds, int flags, int requestId, UserData userData, int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId, int serviceDisplayNameResourceId, boolean showFillDialogIcon, boolean showSaveDialogIcon, FieldClassification[] detectedFieldTypes, PendingIntent dialogPendingIntent)166 private FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState, 167 RemoteViews presentation, InlinePresentation inlinePresentation, 168 InlinePresentation inlineTooltipPresentation, RemoteViews dialogPresentation, 169 RemoteViews dialogHeader, RemoteViews header, RemoteViews footer, 170 IntentSender authentication, AutofillId[] authenticationIds, AutofillId[] ignoredIds, 171 AutofillId[] fillDialogTriggerIds, long disableDuration, 172 AutofillId[] fieldClassificationIds, int flags, int requestId, UserData userData, 173 int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId, 174 int serviceDisplayNameResourceId, boolean showFillDialogIcon, 175 boolean showSaveDialogIcon, 176 FieldClassification[] detectedFieldTypes, PendingIntent dialogPendingIntent) { 177 mDatasets = datasets; 178 mSaveInfo = saveInfo; 179 mClientState = clientState; 180 mPresentation = presentation; 181 mInlinePresentation = inlinePresentation; 182 mInlineTooltipPresentation = inlineTooltipPresentation; 183 mDialogPresentation = dialogPresentation; 184 mDialogHeader = dialogHeader; 185 mHeader = header; 186 mFooter = footer; 187 mAuthentication = authentication; 188 mAuthenticationIds = authenticationIds; 189 mIgnoredIds = ignoredIds; 190 mFillDialogTriggerIds = fillDialogTriggerIds; 191 mDisableDuration = disableDuration; 192 mFieldClassificationIds = fieldClassificationIds; 193 mFlags = flags; 194 mRequestId = requestId; 195 mUserData = userData; 196 mCancelIds = cancelIds; 197 mSupportsInlineSuggestions = supportsInlineSuggestions; 198 mIconResourceId = iconResourceId; 199 mServiceDisplayNameResourceId = serviceDisplayNameResourceId; 200 mShowFillDialogIcon = showFillDialogIcon; 201 mShowSaveDialogIcon = showSaveDialogIcon; 202 mDetectedFieldTypes = detectedFieldTypes; 203 mDialogPendingIntent = dialogPendingIntent; 204 } 205 FillResponse(@onNull Builder builder)206 private FillResponse(@NonNull Builder builder) { 207 mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; 208 mSaveInfo = builder.mSaveInfo; 209 mClientState = builder.mClientState; 210 mPresentation = builder.mPresentation; 211 mInlinePresentation = builder.mInlinePresentation; 212 mInlineTooltipPresentation = builder.mInlineTooltipPresentation; 213 mDialogPresentation = builder.mDialogPresentation; 214 mDialogHeader = builder.mDialogHeader; 215 mHeader = builder.mHeader; 216 mFooter = builder.mFooter; 217 mAuthentication = builder.mAuthentication; 218 mAuthenticationIds = builder.mAuthenticationIds; 219 mFillDialogTriggerIds = builder.mFillDialogTriggerIds; 220 mIgnoredIds = builder.mIgnoredIds; 221 mDisableDuration = builder.mDisableDuration; 222 mFieldClassificationIds = builder.mFieldClassificationIds; 223 mFlags = builder.mFlags; 224 mRequestId = INVALID_REQUEST_ID; 225 mUserData = builder.mUserData; 226 mCancelIds = builder.mCancelIds; 227 mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions; 228 mIconResourceId = builder.mIconResourceId; 229 mServiceDisplayNameResourceId = builder.mServiceDisplayNameResourceId; 230 mShowFillDialogIcon = builder.mShowFillDialogIcon; 231 mShowSaveDialogIcon = builder.mShowSaveDialogIcon; 232 mDetectedFieldTypes = builder.mDetectedFieldTypes; 233 mDialogPendingIntent = builder.mDialogPendingIntent; 234 } 235 236 /** @hide */ 237 @TestApi 238 @NonNull getDetectedFieldClassifications()239 public Set<FieldClassification> getDetectedFieldClassifications() { 240 return Set.of(mDetectedFieldTypes); 241 } 242 243 /** @hide */ getClientState()244 public @Nullable Bundle getClientState() { 245 return mClientState; 246 } 247 248 /** @hide */ getDatasets()249 public @Nullable List<Dataset> getDatasets() { 250 return (mDatasets != null) ? mDatasets.getList() : null; 251 } 252 253 /** @hide */ getSaveInfo()254 public @Nullable SaveInfo getSaveInfo() { 255 return mSaveInfo; 256 } 257 258 /** @hide */ getPresentation()259 public @Nullable RemoteViews getPresentation() { 260 return mPresentation; 261 } 262 263 /** @hide */ getInlinePresentation()264 public @Nullable InlinePresentation getInlinePresentation() { 265 return mInlinePresentation; 266 } 267 268 /** @hide */ getInlineTooltipPresentation()269 public @Nullable InlinePresentation getInlineTooltipPresentation() { 270 return mInlineTooltipPresentation; 271 } 272 273 /** @hide */ getDialogPresentation()274 public @Nullable RemoteViews getDialogPresentation() { 275 return mDialogPresentation; 276 } 277 278 /** @hide */ getDialogHeader()279 public @Nullable RemoteViews getDialogHeader() { 280 return mDialogHeader; 281 } 282 283 /** @hide */ getHeader()284 public @Nullable RemoteViews getHeader() { 285 return mHeader; 286 } 287 288 /** @hide */ getFooter()289 public @Nullable RemoteViews getFooter() { 290 return mFooter; 291 } 292 293 /** @hide */ getAuthentication()294 public @Nullable IntentSender getAuthentication() { 295 return mAuthentication; 296 } 297 298 /** @hide */ getAuthenticationIds()299 public @Nullable AutofillId[] getAuthenticationIds() { 300 return mAuthenticationIds; 301 } 302 303 /** @hide */ getFillDialogTriggerIds()304 public @Nullable AutofillId[] getFillDialogTriggerIds() { 305 return mFillDialogTriggerIds; 306 } 307 308 /** @hide */ getIgnoredIds()309 public @Nullable AutofillId[] getIgnoredIds() { 310 return mIgnoredIds; 311 } 312 313 /** @hide */ getDisableDuration()314 public long getDisableDuration() { 315 return mDisableDuration; 316 } 317 318 /** @hide */ getFieldClassificationIds()319 public @Nullable AutofillId[] getFieldClassificationIds() { 320 return mFieldClassificationIds; 321 } 322 323 /** @hide */ getUserData()324 public @Nullable UserData getUserData() { 325 return mUserData; 326 } 327 328 /** @hide */ getIconResourceId()329 public @DrawableRes int getIconResourceId() { 330 return mIconResourceId; 331 } 332 333 /** @hide */ getServiceDisplayNameResourceId()334 public @StringRes int getServiceDisplayNameResourceId() { 335 return mServiceDisplayNameResourceId; 336 } 337 338 /** @hide */ getShowFillDialogIcon()339 public boolean getShowFillDialogIcon() { 340 return mShowFillDialogIcon; 341 } 342 343 /** @hide */ getShowSaveDialogIcon()344 public boolean getShowSaveDialogIcon() { 345 return mShowSaveDialogIcon; 346 } 347 348 /** @hide */ 349 @TestApi getFlags()350 public int getFlags() { 351 return mFlags; 352 } 353 354 /** 355 * Associates a {@link FillResponse} to a request. 356 * 357 * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}. 358 * 359 * @param requestId The id of the request to associate the response to. 360 * 361 * @hide 362 */ setRequestId(int requestId)363 public void setRequestId(int requestId) { 364 mRequestId = requestId; 365 } 366 367 /** @hide */ getRequestId()368 public int getRequestId() { 369 return mRequestId; 370 } 371 372 /** @hide */ 373 @Nullable getCancelIds()374 public int[] getCancelIds() { 375 return mCancelIds; 376 } 377 378 /** @hide */ supportsInlineSuggestions()379 public boolean supportsInlineSuggestions() { 380 return mSupportsInlineSuggestions; 381 } 382 383 /** 384 * Builder for {@link FillResponse} objects. You must to provide at least 385 * one dataset or set an authentication intent with a presentation view. 386 */ 387 public static final class Builder { 388 private ArrayList<Dataset> mDatasets; 389 private SaveInfo mSaveInfo; 390 private Bundle mClientState; 391 private RemoteViews mPresentation; 392 private InlinePresentation mInlinePresentation; 393 private InlinePresentation mInlineTooltipPresentation; 394 private RemoteViews mDialogPresentation; 395 private RemoteViews mDialogHeader; 396 private RemoteViews mHeader; 397 private RemoteViews mFooter; 398 private IntentSender mAuthentication; 399 private AutofillId[] mAuthenticationIds; 400 private AutofillId[] mIgnoredIds; 401 private long mDisableDuration; 402 private AutofillId[] mFieldClassificationIds; 403 private AutofillId[] mFillDialogTriggerIds; 404 private int mFlags; 405 private boolean mDestroyed; 406 private UserData mUserData; 407 private int[] mCancelIds; 408 private boolean mSupportsInlineSuggestions; 409 private int mIconResourceId; 410 private int mServiceDisplayNameResourceId; 411 private boolean mShowFillDialogIcon = true; 412 private boolean mShowSaveDialogIcon = true; 413 private FieldClassification[] mDetectedFieldTypes; 414 private PendingIntent mDialogPendingIntent; 415 416 /** 417 * Adds a new {@link FieldClassification} to this response, to 418 * help the platform provide more accurate detection results. 419 * 420 * Call this when a field has been detected with a type. 421 * 422 * Altough similiarly named with {@link #setFieldClassificationIds}, 423 * it provides a different functionality - setFieldClassificationIds should 424 * be used when a field is only suspected to be Autofillable. 425 * This method should be used when a field is certainly Autofillable 426 * with a certain type. 427 */ 428 @NonNull setDetectedFieldClassifications( @onNull Set<FieldClassification> fieldInfos)429 public Builder setDetectedFieldClassifications( 430 @NonNull Set<FieldClassification> fieldInfos) { 431 throwIfDestroyed(); 432 throwIfDisableAutofillCalled(); 433 mDetectedFieldTypes = fieldInfos.toArray(new FieldClassification[0]); 434 return this; 435 } 436 437 /** 438 * Triggers a custom UI before autofilling the screen with any data set in this 439 * response. 440 * 441 * <p><b>Note:</b> Although the name of this method suggests that it should be used just for 442 * authentication flow, it can be used for other advanced flows; see {@link AutofillService} 443 * for examples. 444 * 445 * <p>This is typically useful when a user interaction is required to unlock their 446 * data vault if you encrypt the data set labels and data set data. It is recommended 447 * to encrypt only the sensitive data and not the data set labels which would allow 448 * auth on the data set level leading to a better user experience. Note that if you 449 * use sensitive data as a label, for example an email address, then it should also 450 * be encrypted. The provided {@link android.app.PendingIntent intent} must be an 451 * {@link Activity} which implements your authentication flow. Also if you provide an auth 452 * intent you also need to specify the presentation view to be shown in the fill UI 453 * for the user to trigger your authentication flow. 454 * 455 * <p>When a user triggers autofill, the system launches the provided intent 456 * whose extras will have the 457 * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen 458 * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE 459 * client state}. Once you complete your authentication flow you should set the 460 * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the 461 * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra 462 * with the fully populated {@link FillResponse response} (or {@code null} if the screen 463 * cannot be autofilled). 464 * 465 * <p> <b>IMPORTANT</b>: Extras must be non-null on the intent being set for Android 12 466 * otherwise it will cause a crash. Do not use {@link Activity#setResult(int)}, instead use 467 * {@link Activity#setResult(int, Intent) with non-null extras. Consider setting { 468 * @link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} to null or use 469 * {@link Bundle#EMPTY} with {@link Intent#putExtras(Bundle)} on the intent when 470 * finishing activity to avoid crash). </p> 471 * 472 * <p>For example, if you provided an empty {@link FillResponse response} because the 473 * user's data was locked and marked that the response needs an authentication then 474 * in the response returned if authentication succeeds you need to provide all 475 * available data sets some of which may need to be further authenticated, for 476 * example a credit card whose CVV needs to be entered. 477 * 478 * <p>If you provide an authentication intent you must also provide a presentation 479 * which is used to visualize the response for triggering the authentication 480 * flow. 481 * 482 * <p><b>Note:</b> Do not make the provided pending intent 483 * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the 484 * platform needs to fill in the authentication arguments. 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 authentication Intent to an activity with your authentication flow. 490 * @param presentation The presentation to visualize the response. 491 * @param ids id of Views that when focused will display the authentication UI. 492 * 493 * @return This builder. 494 * 495 * @throws IllegalArgumentException if any of the following occurs: 496 * <ul> 497 * <li>{@code ids} is {@code null}</li> 498 * <li>{@code ids} is empty</li> 499 * <li>{@code ids} contains a {@code null} element</li> 500 * <li>both {@code authentication} and {@code presentation} are {@code null}</li> 501 * <li>both {@code authentication} and {@code presentation} are non-{@code null}</li> 502 * </ul> 503 * 504 * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a 505 * {@link #setFooter(RemoteViews) footer} are already set for this builder. 506 * 507 * @see android.app.PendingIntent#getIntentSender() 508 * @deprecated Use 509 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)} 510 * instead. 511 */ 512 @Deprecated 513 @NonNull setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation)514 public Builder setAuthentication(@NonNull AutofillId[] ids, 515 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) { 516 throwIfDestroyed(); 517 throwIfDisableAutofillCalled(); 518 if (mHeader != null || mFooter != null) { 519 throw new IllegalStateException("Already called #setHeader() or #setFooter()"); 520 } 521 522 if (authentication == null ^ presentation == null) { 523 throw new IllegalArgumentException("authentication and presentation" 524 + " must be both non-null or null"); 525 } 526 mAuthentication = authentication; 527 mPresentation = presentation; 528 mAuthenticationIds = assertValid(ids); 529 return this; 530 } 531 532 /** 533 * Triggers a custom UI before autofilling the screen with any data set in this 534 * response. 535 * 536 * <p><b>Note:</b> Although the name of this method suggests that it should be used just for 537 * authentication flow, it can be used for other advanced flows; see {@link AutofillService} 538 * for examples. 539 * 540 * <p>This method is similar to 541 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts 542 * an {@link InlinePresentation} presentation which is required for authenticating through 543 * the inline autofill flow. 544 * 545 * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does 546 * not work with {@link InlinePresentation}.</p> 547 * 548 * @param authentication Intent to an activity with your authentication flow. 549 * @param presentation The presentation to visualize the response. 550 * @param inlinePresentation The inlinePresentation to visualize the response inline. 551 * @param ids id of Views that when focused will display the authentication UI. 552 * 553 * @return This builder. 554 * 555 * @throws IllegalArgumentException if any of the following occurs: 556 * <ul> 557 * <li>{@code ids} is {@code null}</li> 558 * <li>{@code ids} is empty</li> 559 * <li>{@code ids} contains a {@code null} element</li> 560 * <li>both {@code authentication} and {@code presentation} are {@code null}</li> 561 * <li>both {@code authentication} and {@code presentation} are non-{@code null}</li> 562 * <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li> 563 * <li>both {@code authentication} and {@code inlinePresentation} are 564 * non-{@code null}</li> 565 * </ul> 566 * 567 * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a 568 * {@link #setFooter(RemoteViews) footer} are already set for this builder. 569 * 570 * @see android.app.PendingIntent#getIntentSender() 571 * @deprecated Use 572 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)} 573 * instead. 574 */ 575 @Deprecated 576 @NonNull setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation)577 public Builder setAuthentication(@NonNull AutofillId[] ids, 578 @Nullable IntentSender authentication, @Nullable RemoteViews presentation, 579 @Nullable InlinePresentation inlinePresentation) { 580 return setAuthentication(ids, authentication, presentation, inlinePresentation, null); 581 } 582 583 /** 584 * Triggers a custom UI before autofilling the screen with any data set in this 585 * response. 586 * 587 * <p>This method like 588 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)} 589 * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip. 590 * 591 * @deprecated Use 592 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)} 593 * instead. 594 */ 595 @Deprecated 596 @NonNull setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation)597 public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids, 598 @Nullable IntentSender authentication, @Nullable RemoteViews presentation, 599 @Nullable InlinePresentation inlinePresentation, 600 @Nullable InlinePresentation inlineTooltipPresentation) { 601 throwIfDestroyed(); 602 throwIfDisableAutofillCalled(); 603 return setAuthentication(ids, authentication, presentation, 604 inlinePresentation, inlineTooltipPresentation, null); 605 } 606 607 /** 608 * Triggers a custom UI before autofilling the screen with any data set in this 609 * response. 610 * 611 * <p><b>Note:</b> Although the name of this method suggests that it should be used just for 612 * authentication flow, it can be used for other advanced flows; see {@link AutofillService} 613 * for examples. 614 * 615 * <p>This is typically useful when a user interaction is required to unlock their 616 * data vault if you encrypt the data set labels and data set data. It is recommended 617 * to encrypt only the sensitive data and not the data set labels which would allow 618 * auth on the data set level leading to a better user experience. Note that if you 619 * use sensitive data as a label, for example an email address, then it should also 620 * be encrypted. The provided {@link android.app.PendingIntent intent} must be an 621 * {@link Activity} which implements your authentication flow. Also if you provide an auth 622 * intent you also need to specify the presentation view to be shown in the fill UI 623 * for the user to trigger your authentication flow. 624 * 625 * <p>When a user triggers autofill, the system launches the provided intent 626 * whose extras will have the 627 * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen 628 * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE 629 * client state}. Once you complete your authentication flow you should set the 630 * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the 631 * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra 632 * with the fully populated {@link FillResponse response} (or {@code null} if the screen 633 * cannot be autofilled). 634 * 635 * <p>For example, if you provided an empty {@link FillResponse response} because the 636 * user's data was locked and marked that the response needs an authentication then 637 * in the response returned if authentication succeeds you need to provide all 638 * available data sets some of which may need to be further authenticated, for 639 * example a credit card whose CVV needs to be entered. 640 * 641 * <p>If you provide an authentication intent you must also provide a presentation 642 * which is used to visualize the response for triggering the authentication 643 * flow. 644 * 645 * <p><b>Note:</b> Do not make the provided pending intent 646 * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the 647 * platform needs to fill in the authentication arguments. 648 * 649 * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does 650 * not work with {@link InlinePresentation}.</p> 651 * 652 * @param ids id of Views that when focused will display the authentication UI. 653 * @param authentication Intent to an activity with your authentication flow. 654 * @param presentations The presentations to visualize the response. 655 * 656 * @throws IllegalArgumentException if any of the following occurs: 657 * <ul> 658 * <li>{@code ids} is {@code null}</li> 659 * <li>{@code ids} is empty</li> 660 * <li>{@code ids} contains a {@code null} element</li> 661 * <li>{@code authentication} is {@code null}, but either or both of 662 * {@code presentations.getPresentation()} and 663 * {@code presentations.getInlinePresentation()} is non-{@code null}</li> 664 * <li>{@code authentication} is non-{{@code null}, but both 665 * {@code presentations.getPresentation()} and 666 * {@code presentations.getInlinePresentation()} are {@code null}</li> 667 * </ul> 668 * 669 * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a 670 * {@link #setFooter(RemoteViews) footer} are already set for this builder. 671 * 672 * @return This builder. 673 */ 674 @NonNull setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable Presentations presentations)675 public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids, 676 @Nullable IntentSender authentication, 677 @Nullable Presentations presentations) { 678 throwIfDestroyed(); 679 throwIfDisableAutofillCalled(); 680 if (presentations == null) { 681 return setAuthentication(ids, authentication, null, null, null, null); 682 } 683 return setAuthentication(ids, authentication, 684 presentations.getMenuPresentation(), 685 presentations.getInlinePresentation(), 686 presentations.getInlineTooltipPresentation(), 687 presentations.getDialogPresentation()); 688 } 689 690 /** 691 * Triggers a custom UI before autofilling the screen with any data set in this 692 * response. 693 */ 694 @NonNull setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation, @Nullable RemoteViews dialogPresentation)695 private Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids, 696 @Nullable IntentSender authentication, @Nullable RemoteViews presentation, 697 @Nullable InlinePresentation inlinePresentation, 698 @Nullable InlinePresentation inlineTooltipPresentation, 699 @Nullable RemoteViews dialogPresentation) { 700 throwIfDestroyed(); 701 throwIfDisableAutofillCalled(); 702 if (mHeader != null || mFooter != null) { 703 throw new IllegalStateException("Already called #setHeader() or #setFooter()"); 704 } 705 706 if (authentication == null ^ (presentation == null && inlinePresentation == null)) { 707 throw new IllegalArgumentException("authentication and presentation " 708 + "(dropdown or inline), must be both non-null or null"); 709 } 710 mAuthentication = authentication; 711 mPresentation = presentation; 712 mInlinePresentation = inlinePresentation; 713 mInlineTooltipPresentation = inlineTooltipPresentation; 714 mDialogPresentation = dialogPresentation; 715 mAuthenticationIds = assertValid(ids); 716 return this; 717 } 718 719 /** 720 * Specifies views that should not trigger new 721 * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, 722 * FillCallback)} requests. 723 * 724 * <p>This is typically used when the service cannot autofill the view; for example, a 725 * text field representing the result of a Captcha challenge. 726 */ 727 @NonNull setIgnoredIds(AutofillId...ids)728 public Builder setIgnoredIds(AutofillId...ids) { 729 throwIfDestroyed(); 730 mIgnoredIds = ids; 731 return this; 732 } 733 734 /** 735 * Adds a new {@link Dataset} to this response. 736 * 737 * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of 738 * datasets is limited by the Binder transaction size, so it's recommended to keep it 739 * small (in the range of 10-20 at most) and use pagination by adding a fake 740 * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end 741 * with a presentation string like "Next 10" that would return a new {@link FillResponse} 742 * with the next 10 datasets, and so on. This limitation was lifted on 743 * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction 744 * size can still be reached if each dataset itself is too big. 745 * 746 * @return This builder. 747 */ 748 @NonNull addDataset(@ullable Dataset dataset)749 public Builder addDataset(@Nullable Dataset dataset) { 750 throwIfDestroyed(); 751 throwIfDisableAutofillCalled(); 752 if (dataset == null) { 753 return this; 754 } 755 if (mDatasets == null) { 756 mDatasets = new ArrayList<>(); 757 } 758 if (!mDatasets.add(dataset)) { 759 return this; 760 } 761 return this; 762 } 763 764 /** 765 * @hide 766 */ 767 @NonNull setDatasets(ArrayList<Dataset> dataset)768 public Builder setDatasets(ArrayList<Dataset> dataset) { 769 mDatasets = dataset; 770 return this; 771 } 772 773 /** 774 * Sets the {@link SaveInfo} associated with this response. 775 * 776 * @return This builder. 777 */ setSaveInfo(@onNull SaveInfo saveInfo)778 public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) { 779 throwIfDestroyed(); 780 throwIfDisableAutofillCalled(); 781 mSaveInfo = saveInfo; 782 return this; 783 } 784 785 /** 786 * Sets a bundle with state that is passed to subsequent APIs that manipulate this response. 787 * 788 * <p>You can use this bundle to store intermediate state that is passed to subsequent calls 789 * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, 790 * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and 791 * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}. 792 * 793 * <p>If this method is called on multiple {@link FillResponse} objects for the same 794 * screen, just the latest bundle is passed back to the service. 795 * 796 * @param clientState The custom client state. 797 * @return This builder. 798 */ 799 @NonNull setClientState(@ullable Bundle clientState)800 public Builder setClientState(@Nullable Bundle clientState) { 801 throwIfDestroyed(); 802 throwIfDisableAutofillCalled(); 803 mClientState = clientState; 804 return this; 805 } 806 807 /** 808 * Sets which fields are used for 809 * <a href="AutofillService.html#FieldClassification">field classification</a> 810 * 811 * <p><b>Note:</b> This method automatically adds the 812 * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}. 813 814 * @throws IllegalArgumentException is length of {@code ids} args is more than 815 * {@link UserData#getMaxFieldClassificationIdsSize()}. 816 * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was 817 * already called. 818 * @throws NullPointerException if {@code ids} or any element on it is {@code null}. 819 */ 820 @NonNull setFieldClassificationIds(@onNull AutofillId... ids)821 public Builder setFieldClassificationIds(@NonNull AutofillId... ids) { 822 throwIfDestroyed(); 823 throwIfDisableAutofillCalled(); 824 Preconditions.checkArrayElementsNotNull(ids, "ids"); 825 Preconditions.checkArgumentInRange(ids.length, 1, 826 UserData.getMaxFieldClassificationIdsSize(), "ids length"); 827 mFieldClassificationIds = ids; 828 mFlags |= FLAG_TRACK_CONTEXT_COMMITED; 829 return this; 830 } 831 832 /** 833 * Sets flags changing the response behavior. 834 * 835 * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and 836 * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}. 837 * 838 * @return This builder. 839 */ 840 @NonNull setFlags(@illResponseFlags int flags)841 public Builder setFlags(@FillResponseFlags int flags) { 842 throwIfDestroyed(); 843 mFlags = Preconditions.checkFlagsArgument(flags, 844 FLAG_TRACK_CONTEXT_COMMITED 845 | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL 846 | FLAG_CREDENTIAL_MANAGER_RESPONSE); 847 return this; 848 } 849 850 /** 851 * Disables autofill for the app or activity. 852 * 853 * <p>This method is useful to optimize performance in cases where the service knows it 854 * can not autofill an app—for example, when the service has a list of "denylisted" 855 * apps such as office suites. 856 * 857 * <p>By default, it disables autofill for all activities in the app, unless the response is 858 * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}. 859 * 860 * <p>Autofill for the app or activity is automatically re-enabled after any of the 861 * following conditions: 862 * 863 * <ol> 864 * <li>{@code duration} milliseconds have passed. 865 * <li>The autofill service for the user has changed. 866 * <li>The device has rebooted. 867 * </ol> 868 * 869 * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain 870 * disabled for autofill until they finish and restart. 871 * 872 * @param duration duration to disable autofill, in milliseconds. 873 * 874 * @return this builder 875 * 876 * @throws IllegalArgumentException if {@code duration} is not a positive number. 877 * @throws IllegalStateException if either {@link #addDataset(Dataset)}, 878 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}, 879 * {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or 880 * {@link #setFieldClassificationIds(AutofillId...)} was already called. 881 */ 882 @NonNull disableAutofill(long duration)883 public Builder disableAutofill(long duration) { 884 throwIfDestroyed(); 885 if (duration <= 0) { 886 throw new IllegalArgumentException("duration must be greater than 0"); 887 } 888 if (mAuthentication != null || mDatasets != null || mSaveInfo != null 889 || mFieldClassificationIds != null || mClientState != null) { 890 throw new IllegalStateException("disableAutofill() must be the only method called"); 891 } 892 893 mDisableDuration = duration; 894 return this; 895 } 896 897 /** 898 * Overwrites Save/Fill dialog header icon with a specific one specified by resource id. 899 * The image is pulled from the package, so id should be defined in the manifest. 900 * 901 * @param id {@link android.graphics.drawable.Drawable} resource id of the icon to be used. 902 * A value of 0 indicates to use the default header icon. 903 * 904 * @return this builder 905 */ 906 @NonNull setIconResourceId(@rawableRes int id)907 public Builder setIconResourceId(@DrawableRes int id) { 908 throwIfDestroyed(); 909 910 mIconResourceId = id; 911 return this; 912 } 913 914 /** 915 * Overrides the service name in the Save Dialog header with a specific string defined 916 * in the service provider's manifest.xml 917 * 918 * @param id Resoure Id of the custom string defined in the provider's manifest. If set 919 * to 0, the default name will be used. 920 * 921 * @return this builder 922 */ 923 @NonNull setServiceDisplayNameResourceId(@tringRes int id)924 public Builder setServiceDisplayNameResourceId(@StringRes int id) { 925 throwIfDestroyed(); 926 927 mServiceDisplayNameResourceId = id; 928 return this; 929 } 930 931 /** 932 * Whether or not to show the Autofill provider icon inside of the Fill Dialog 933 * 934 * @param show True to show, false to hide. Defaults to true. 935 * 936 * @return this builder 937 */ 938 @NonNull setShowFillDialogIcon(boolean show)939 public Builder setShowFillDialogIcon(boolean show) { 940 throwIfDestroyed(); 941 942 mShowFillDialogIcon = show; 943 return this; 944 } 945 946 /** 947 * Whether or not to show the Autofill provider icon inside of the Save Dialog 948 * 949 * @param show True to show, false to hide. Defaults to true. 950 * 951 * @return this builder 952 */ 953 @NonNull setShowSaveDialogIcon(boolean show)954 public Builder setShowSaveDialogIcon(boolean show) { 955 throwIfDestroyed(); 956 957 mShowSaveDialogIcon = show; 958 return this; 959 } 960 961 /** 962 * Sets a header to be shown as the first element in the list of datasets. 963 * 964 * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset}, 965 * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this 966 * method should only be used on {@link FillResponse FillResponses} that do not require 967 * authentication (as the header could have been set directly in the main presentation in 968 * these cases). 969 * 970 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color 971 * or background color: Autofill on different platforms may have different themes. 972 * 973 * @param header a presentation to represent the header. This presentation is not clickable 974 * —calling 975 * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would 976 * have no effect. 977 * 978 * @return this builder 979 * 980 * @throws IllegalStateException if an 981 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations) 982 * authentication} was already set for this builder. 983 */ 984 // TODO(b/69796626): make it sticky / update javadoc 985 @NonNull setHeader(@onNull RemoteViews header)986 public Builder setHeader(@NonNull RemoteViews header) { 987 throwIfDestroyed(); 988 throwIfAuthenticationCalled(); 989 mHeader = Objects.requireNonNull(header); 990 return this; 991 } 992 993 /** 994 * Sets a footer to be shown as the last element in the list of datasets. 995 * 996 * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset}, 997 * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this 998 * method should only be used on {@link FillResponse FillResponses} that do not require 999 * authentication (as the footer could have been set directly in the main presentation in 1000 * these cases). 1001 * 1002 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color 1003 * or background color: Autofill on different platforms may have different themes. 1004 * 1005 * @param footer a presentation to represent the footer. This presentation is not clickable 1006 * —calling 1007 * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would 1008 * have no effect. 1009 * 1010 * @return this builder 1011 * 1012 * @throws IllegalStateException if the FillResponse 1013 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations) 1014 * requires authentication}. 1015 */ 1016 // TODO(b/69796626): make it sticky / update javadoc 1017 @NonNull setFooter(@onNull RemoteViews footer)1018 public Builder setFooter(@NonNull RemoteViews footer) { 1019 throwIfDestroyed(); 1020 throwIfAuthenticationCalled(); 1021 mFooter = Objects.requireNonNull(footer); 1022 return this; 1023 } 1024 1025 /** 1026 * Sets a specific {@link UserData} for field classification for this request only. 1027 * 1028 * <p>Any fields in this UserData will override corresponding fields in the generic 1029 * UserData object 1030 * 1031 * @return this builder 1032 * @throws IllegalStateException if the FillResponse 1033 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations) 1034 * requires authentication}. 1035 */ 1036 @NonNull setUserData(@onNull UserData userData)1037 public Builder setUserData(@NonNull UserData userData) { 1038 throwIfDestroyed(); 1039 throwIfAuthenticationCalled(); 1040 mUserData = Objects.requireNonNull(userData); 1041 return this; 1042 } 1043 1044 /** 1045 * Sets target resource IDs of the child view in {@link RemoteViews Presentation Template} 1046 * which will cancel the session when clicked. 1047 * Those targets will be respectively applied to a child of the header, footer and 1048 * each {@link Dataset}. 1049 * 1050 * @param ids array of the resource id. Empty list or non-existing id has no effect. 1051 * 1052 * @return this builder 1053 * 1054 * @throws IllegalStateException if {@link #build()} was already called. 1055 */ 1056 @NonNull setPresentationCancelIds(@ullable int[] ids)1057 public Builder setPresentationCancelIds(@Nullable int[] ids) { 1058 throwIfDestroyed(); 1059 mCancelIds = ids; 1060 return this; 1061 } 1062 1063 /** 1064 * Sets the presentation of header in fill dialog UI. The header should have 1065 * a prompt for what datasets are shown in the dialog. If this is not set, 1066 * the dialog only shows your application icon. 1067 * 1068 * More details about the fill dialog, see 1069 * <a href="Dataset.html#FillDialogUI">fill dialog UI</a> 1070 */ 1071 @NonNull setDialogHeader(@onNull RemoteViews header)1072 public Builder setDialogHeader(@NonNull RemoteViews header) { 1073 throwIfDestroyed(); 1074 Objects.requireNonNull(header); 1075 mDialogHeader = header; 1076 return this; 1077 } 1078 1079 /** 1080 * Sets which fields are used for the fill dialog UI. 1081 * 1082 * More details about the fill dialog, see 1083 * <a href="Dataset.html#FillDialogUI">fill dialog UI</a> 1084 * 1085 * @throws IllegalStateException if {@link #build()} was already called. 1086 * @throws NullPointerException if {@code ids} or any element on it is {@code null}. 1087 */ 1088 @NonNull setFillDialogTriggerIds(@onNull AutofillId... ids)1089 public Builder setFillDialogTriggerIds(@NonNull AutofillId... ids) { 1090 throwIfDestroyed(); 1091 Preconditions.checkArrayElementsNotNull(ids, "ids"); 1092 mFillDialogTriggerIds = ids; 1093 return this; 1094 } 1095 1096 /** 1097 * Sets credential dialog pending intent. Framework will use the intent to launch the 1098 * selector UI. A replacement for previous fill bottom sheet. 1099 * 1100 * @throws IllegalStateException if {@link #build()} was already called. 1101 * @throws NullPointerException if {@code pendingIntent} is {@code null}. 1102 * 1103 * @hide 1104 */ 1105 @NonNull setDialogPendingIntent(@onNull PendingIntent pendingIntent)1106 public Builder setDialogPendingIntent(@NonNull PendingIntent pendingIntent) { 1107 throwIfDestroyed(); 1108 Preconditions.checkNotNull(pendingIntent, 1109 "can't pass a null object to setDialogPendingIntent"); 1110 mDialogPendingIntent = pendingIntent; 1111 return this; 1112 } 1113 1114 /** 1115 * Builds a new {@link FillResponse} instance. 1116 * 1117 * @throws IllegalStateException if any of the following conditions occur: 1118 * <ol> 1119 * <li>{@link #build()} was already called. 1120 * <li>No call was made to {@link #addDataset(Dataset)}, 1121 * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}, 1122 * {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)}, 1123 * {@link #setClientState(Bundle)}, 1124 * or {@link #setFieldClassificationIds(AutofillId...)}. 1125 * <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called 1126 * without any previous calls to {@link #addDataset(Dataset)}. 1127 * </ol> 1128 * 1129 * @return A built response. 1130 */ 1131 @NonNull build()1132 public FillResponse build() { 1133 throwIfDestroyed(); 1134 if (mAuthentication == null && mDatasets == null && mSaveInfo == null 1135 && mDisableDuration == 0 && mFieldClassificationIds == null 1136 && mClientState == null) { 1137 throw new IllegalStateException("need to provide: at least one DataSet, or a " 1138 + "SaveInfo, or an authentication with a presentation, " 1139 + "or a FieldsDetection, or a client state, or disable autofill"); 1140 } 1141 if (mDatasets == null && (mHeader != null || mFooter != null)) { 1142 throw new IllegalStateException( 1143 "must add at least 1 dataset when using header or footer"); 1144 } 1145 1146 if (mDatasets != null) { 1147 for (final Dataset dataset : mDatasets) { 1148 if (dataset.getFieldInlinePresentation(0) != null) { 1149 mSupportsInlineSuggestions = true; 1150 break; 1151 } 1152 } 1153 } else if (mInlinePresentation != null) { 1154 mSupportsInlineSuggestions = true; 1155 } 1156 1157 mDestroyed = true; 1158 return new FillResponse(this); 1159 } 1160 throwIfDestroyed()1161 private void throwIfDestroyed() { 1162 if (mDestroyed) { 1163 throw new IllegalStateException("Already called #build()"); 1164 } 1165 } 1166 throwIfDisableAutofillCalled()1167 private void throwIfDisableAutofillCalled() { 1168 if (mDisableDuration > 0) { 1169 throw new IllegalStateException("Already called #disableAutofill()"); 1170 } 1171 } 1172 throwIfAuthenticationCalled()1173 private void throwIfAuthenticationCalled() { 1174 if (mAuthentication != null) { 1175 throw new IllegalStateException("Already called #setAuthentication()"); 1176 } 1177 } 1178 } 1179 1180 ///////////////////////////////////// 1181 // Object "contract" methods. // 1182 ///////////////////////////////////// 1183 @Override toString()1184 public String toString() { 1185 if (!sDebug) return super.toString(); 1186 1187 // TODO: create a dump() method instead 1188 final StringBuilder builder = new StringBuilder( 1189 "FillResponse : [mRequestId=" + mRequestId); 1190 if (mDatasets != null) { 1191 builder.append(", datasets=").append(mDatasets.getList()); 1192 } 1193 if (mSaveInfo != null) { 1194 builder.append(", saveInfo=").append(mSaveInfo); 1195 } 1196 if (mClientState != null) { 1197 builder.append(", hasClientState"); 1198 } 1199 if (mPresentation != null) { 1200 builder.append(", hasPresentation"); 1201 } 1202 if (mInlinePresentation != null) { 1203 builder.append(", hasInlinePresentation"); 1204 } 1205 if (mInlineTooltipPresentation != null) { 1206 builder.append(", hasInlineTooltipPresentation"); 1207 } 1208 if (mDialogPresentation != null) { 1209 builder.append(", hasDialogPresentation"); 1210 } 1211 if (mDialogHeader != null) { 1212 builder.append(", hasDialogHeader"); 1213 } 1214 if (mHeader != null) { 1215 builder.append(", hasHeader"); 1216 } 1217 if (mFooter != null) { 1218 builder.append(", hasFooter"); 1219 } 1220 if (mAuthentication != null) { 1221 builder.append(", hasAuthentication"); 1222 } 1223 if (mDialogPendingIntent != null) { 1224 builder.append(", hasDialogPendingIntent"); 1225 } 1226 if (mAuthenticationIds != null) { 1227 builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds)); 1228 } 1229 if (mFillDialogTriggerIds != null) { 1230 builder.append(", fillDialogTriggerIds=") 1231 .append(Arrays.toString(mFillDialogTriggerIds)); 1232 } 1233 builder.append(", disableDuration=").append(mDisableDuration); 1234 if (mFlags != 0) { 1235 builder.append(", flags=").append(mFlags); 1236 } 1237 if (mFieldClassificationIds != null) { 1238 builder.append(Arrays.toString(mFieldClassificationIds)); 1239 } 1240 if (mUserData != null) { 1241 builder.append(", userData=").append(mUserData); 1242 } 1243 if (mCancelIds != null) { 1244 builder.append(", mCancelIds=").append(mCancelIds.length); 1245 } 1246 builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions); 1247 return builder.append("]").toString(); 1248 } 1249 1250 ///////////////////////////////////// 1251 // Parcelable "contract" methods. // 1252 ///////////////////////////////////// 1253 1254 @Override describeContents()1255 public int describeContents() { 1256 return 0; 1257 } 1258 1259 @Override writeToParcel(Parcel parcel, int flags)1260 public void writeToParcel(Parcel parcel, int flags) { 1261 parcel.writeParcelable(mDatasets, flags); 1262 parcel.writeParcelable(mSaveInfo, flags); 1263 parcel.writeParcelable(mClientState, flags); 1264 parcel.writeParcelableArray(mAuthenticationIds, flags); 1265 parcel.writeParcelable(mAuthentication, flags); 1266 parcel.writeParcelable(mPresentation, flags); 1267 parcel.writeParcelable(mInlinePresentation, flags); 1268 parcel.writeParcelable(mInlineTooltipPresentation, flags); 1269 parcel.writeParcelable(mDialogPresentation, flags); 1270 parcel.writeParcelable(mDialogHeader, flags); 1271 parcel.writeParcelable(mDialogPendingIntent, flags); 1272 parcel.writeParcelableArray(mFillDialogTriggerIds, flags); 1273 parcel.writeParcelable(mHeader, flags); 1274 parcel.writeParcelable(mFooter, flags); 1275 parcel.writeParcelable(mUserData, flags); 1276 parcel.writeParcelableArray(mIgnoredIds, flags); 1277 parcel.writeLong(mDisableDuration); 1278 parcel.writeParcelableArray(mFieldClassificationIds, flags); 1279 parcel.writeParcelableArray(mDetectedFieldTypes, flags); 1280 parcel.writeInt(mIconResourceId); 1281 parcel.writeInt(mServiceDisplayNameResourceId); 1282 parcel.writeBoolean(mShowFillDialogIcon); 1283 parcel.writeBoolean(mShowSaveDialogIcon); 1284 parcel.writeInt(mFlags); 1285 parcel.writeIntArray(mCancelIds); 1286 parcel.writeInt(mRequestId); 1287 } 1288 1289 public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR = 1290 new Parcelable.Creator<FillResponse>() { 1291 @Override 1292 public FillResponse createFromParcel(Parcel parcel) { 1293 // Always go through the builder to ensure the data ingested by 1294 // the system obeys the contract of the builder to avoid attacks 1295 // using specially crafted parcels. 1296 final Builder builder = new Builder(); 1297 final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class); 1298 final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null; 1299 final int datasetCount = (datasets != null) ? datasets.size() : 0; 1300 for (int i = 0; i < datasetCount; i++) { 1301 builder.addDataset(datasets.get(i)); 1302 } 1303 builder.setSaveInfo(parcel.readParcelable(null, android.service.autofill.SaveInfo.class)); 1304 builder.setClientState(parcel.readParcelable(null, android.os.Bundle.class)); 1305 1306 // Sets authentication state. 1307 final AutofillId[] authenticationIds = parcel.readParcelableArray(null, 1308 AutofillId.class); 1309 final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class); 1310 final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class); 1311 final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); 1312 final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); 1313 final RemoteViews dialogPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class); 1314 if (authenticationIds != null) { 1315 builder.setAuthentication(authenticationIds, authentication, presentation, 1316 inlinePresentation, inlineTooltipPresentation, dialogPresentation); 1317 } 1318 final RemoteViews dialogHeader = parcel.readParcelable(null, android.widget.RemoteViews.class); 1319 if (dialogHeader != null) { 1320 builder.setDialogHeader(dialogHeader); 1321 } 1322 final PendingIntent dialogPendingIntent = parcel.readParcelable(null, 1323 PendingIntent.class); 1324 if (dialogPendingIntent != null) { 1325 builder.setDialogPendingIntent(dialogPendingIntent); 1326 } 1327 final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class); 1328 if (triggerIds != null) { 1329 builder.setFillDialogTriggerIds(triggerIds); 1330 } 1331 final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class); 1332 if (header != null) { 1333 builder.setHeader(header); 1334 } 1335 final RemoteViews footer = parcel.readParcelable(null, android.widget.RemoteViews.class); 1336 if (footer != null) { 1337 builder.setFooter(footer); 1338 } 1339 final UserData userData = parcel.readParcelable(null, android.service.autofill.UserData.class); 1340 if (userData != null) { 1341 builder.setUserData(userData); 1342 } 1343 1344 builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class)); 1345 final long disableDuration = parcel.readLong(); 1346 if (disableDuration > 0) { 1347 builder.disableAutofill(disableDuration); 1348 } 1349 final AutofillId[] fieldClassifactionIds = 1350 parcel.readParcelableArray(null, AutofillId.class); 1351 if (fieldClassifactionIds != null) { 1352 builder.setFieldClassificationIds(fieldClassifactionIds); 1353 } 1354 1355 final FieldClassification[] detectedFields = 1356 parcel.readParcelableArray(null, FieldClassification.class); 1357 if (detectedFields != null) { 1358 builder.setDetectedFieldClassifications(Set.of(detectedFields)); 1359 } 1360 1361 builder.setIconResourceId(parcel.readInt()); 1362 builder.setServiceDisplayNameResourceId(parcel.readInt()); 1363 builder.setShowFillDialogIcon(parcel.readBoolean()); 1364 builder.setShowSaveDialogIcon(parcel.readBoolean()); 1365 builder.setFlags(parcel.readInt()); 1366 final int[] cancelIds = parcel.createIntArray(); 1367 builder.setPresentationCancelIds(cancelIds); 1368 1369 final FillResponse response = builder.build(); 1370 response.setRequestId(parcel.readInt()); 1371 1372 return response; 1373 } 1374 1375 @Override 1376 public FillResponse[] newArray(int size) { 1377 return new FillResponse[size]; 1378 } 1379 }; 1380 } 1381