AutofillManager.java revision 7f33cd350be4278ce5d4ef460c11e4dbaf9c473b
1/* 2 * Copyright (C) 2017 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.view.autofill; 18 19import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; 20import static android.view.autofill.Helper.sDebug; 21import static android.view.autofill.Helper.sVerbose; 22 23import android.annotation.IntDef; 24import android.annotation.NonNull; 25import android.annotation.Nullable; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentSender; 29import android.graphics.Rect; 30import android.metrics.LogMaker; 31import android.os.Bundle; 32import android.os.Parcelable; 33import android.os.RemoteException; 34import android.service.autofill.AutofillService; 35import android.service.autofill.FillEventHistory; 36import android.util.ArrayMap; 37import android.util.ArraySet; 38import android.util.Log; 39import android.util.SparseArray; 40import android.view.View; 41 42import com.android.internal.annotations.GuardedBy; 43import com.android.internal.logging.MetricsLogger; 44import com.android.internal.logging.nano.MetricsProto; 45 46import java.lang.annotation.Retention; 47import java.lang.annotation.RetentionPolicy; 48import java.lang.ref.WeakReference; 49import java.util.ArrayList; 50import java.util.List; 51import java.util.Objects; 52 53/** 54 * App entry point to the Autofill Framework. 55 * 56 * <p>It is safe to call into this from any thread. 57 */ 58public final class AutofillManager { 59 60 private static final String TAG = "AutofillManager"; 61 62 /** 63 * Intent extra: The assist structure which captures the filled screen. 64 * 65 * <p> 66 * Type: {@link android.app.assist.AssistStructure} 67 */ 68 public static final String EXTRA_ASSIST_STRUCTURE = 69 "android.view.autofill.extra.ASSIST_STRUCTURE"; 70 71 /** 72 * Intent extra: The result of an authentication operation. It is 73 * either a fully populated {@link android.service.autofill.FillResponse} 74 * or a fully populated {@link android.service.autofill.Dataset} if 75 * a response or a dataset is being authenticated respectively. 76 * 77 * <p> 78 * Type: {@link android.service.autofill.FillResponse} or a 79 * {@link android.service.autofill.Dataset} 80 */ 81 public static final String EXTRA_AUTHENTICATION_RESULT = 82 "android.view.autofill.extra.AUTHENTICATION_RESULT"; 83 84 /** 85 * Intent extra: The optional extras provided by the 86 * {@link android.service.autofill.AutofillService}. 87 * 88 * <p>For example, when the service responds to a {@link 89 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with 90 * a {@code FillResponse} that requires authentication, the Intent that launches the 91 * service authentication will contain the Bundle set by 92 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra. 93 * 94 * <p> 95 * Type: {@link android.os.Bundle} 96 */ 97 public static final String EXTRA_CLIENT_STATE = 98 "android.view.autofill.extra.CLIENT_STATE"; 99 100 static final String SESSION_ID_TAG = "android:sessionId"; 101 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; 102 103 /** @hide */ public static final int ACTION_START_SESSION = 1; 104 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2; 105 /** @hide */ public static final int ACTION_VIEW_EXITED = 3; 106 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4; 107 108 109 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1; 110 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; 111 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; 112 113 /** Which bits in an authentication id are used for the dataset id */ 114 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF; 115 /** How many bits in an authentication id are used for the dataset id */ 116 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16; 117 /** @hide The index for an undefined data set */ 118 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF; 119 120 /** 121 * Makes an authentication id from a request id and a dataset id. 122 * 123 * @param requestId The request id. 124 * @param datasetId The dataset id. 125 * @return The authentication id. 126 * @hide 127 */ 128 public static int makeAuthenticationId(int requestId, int datasetId) { 129 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT) 130 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK); 131 } 132 133 /** 134 * Gets the request id from an authentication id. 135 * 136 * @param authRequestId The authentication id. 137 * @return The request id. 138 * @hide 139 */ 140 public static int getRequestIdFromAuthenticationId(int authRequestId) { 141 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT); 142 } 143 144 /** 145 * Gets the dataset id from an authentication id. 146 * 147 * @param authRequestId The authentication id. 148 * @return The dataset id. 149 * @hide 150 */ 151 public static int getDatasetIdFromAuthenticationId(int authRequestId) { 152 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK); 153 } 154 155 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 156 157 /** 158 * There is currently no session running. 159 * {@hide} 160 */ 161 public static final int NO_SESSION = Integer.MIN_VALUE; 162 163 private final IAutoFillManager mService; 164 165 private final Object mLock = new Object(); 166 167 @GuardedBy("mLock") 168 private IAutoFillManagerClient mServiceClient; 169 170 @GuardedBy("mLock") 171 private AutofillCallback mCallback; 172 173 private final Context mContext; 174 175 @GuardedBy("mLock") 176 private int mSessionId = NO_SESSION; 177 178 @GuardedBy("mLock") 179 private boolean mEnabled; 180 181 /** If a view changes to this mapping the autofill operation was successful */ 182 @GuardedBy("mLock") 183 @Nullable private ParcelableMap mLastAutofilledData; 184 185 /** If view tracking is enabled, contains the tracking state */ 186 @GuardedBy("mLock") 187 @Nullable private TrackedViews mTrackedViews; 188 189 /** @hide */ 190 public interface AutofillClient { 191 /** 192 * Asks the client to start an authentication flow. 193 * 194 * @param authenticationId A unique id of the authentication operation. 195 * @param intent The authentication intent. 196 * @param fillInIntent The authentication fill-in intent. 197 */ 198 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent, 199 Intent fillInIntent); 200 201 /** 202 * Tells the client this manager has state to be reset. 203 */ 204 void autofillCallbackResetableStateAvailable(); 205 206 /** 207 * Request showing the autofill UI. 208 * 209 * @param anchor The real view the UI needs to anchor to. 210 * @param width The width of the fill UI content. 211 * @param height The height of the fill UI content. 212 * @param virtualBounds The bounds of the virtual decendant of the anchor. 213 * @param presenter The presenter that controls the fill UI window. 214 * @return Whether the UI was shown. 215 */ 216 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, 217 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); 218 219 /** 220 * Request hiding the autofill UI. 221 * 222 * @return Whether the UI was hidden. 223 */ 224 boolean autofillCallbackRequestHideFillUi(); 225 226 /** 227 * Checks if views are currently attached and visible. 228 * 229 * @return And array with {@code true} iff the view is attached or visible 230 */ 231 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId); 232 233 /** 234 * Checks is the client is currently visible as understood by autofill. 235 * 236 * @return {@code true} if the client is currently visible 237 */ 238 boolean isVisibleForAutofill(); 239 240 /** 241 * Find views by traversing the hierarchies of the client. 242 * 243 * @param viewIds The accessibility ids of the views to find 244 * 245 * @return And array containing the views, or {@code null} if not found 246 */ 247 @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds); 248 } 249 250 /** 251 * @hide 252 */ 253 public AutofillManager(Context context, IAutoFillManager service) { 254 mContext = context; 255 mService = service; 256 } 257 258 /** 259 * Restore state after activity lifecycle 260 * 261 * @param savedInstanceState The state to be restored 262 * 263 * {@hide} 264 */ 265 public void onCreate(Bundle savedInstanceState) { 266 if (!hasAutofillFeature()) { 267 return; 268 } 269 synchronized (mLock) { 270 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG); 271 272 if (mSessionId != NO_SESSION) { 273 Log.w(TAG, "New session was started before onCreate()"); 274 return; 275 } 276 277 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION); 278 279 if (mSessionId != NO_SESSION) { 280 ensureServiceClientAddedIfNeededLocked(); 281 282 final AutofillClient client = getClientLocked(); 283 if (client != null) { 284 try { 285 final boolean sessionWasRestored = mService.restoreSession(mSessionId, 286 mContext.getActivityToken(), mServiceClient.asBinder()); 287 288 if (!sessionWasRestored) { 289 Log.w(TAG, "Session " + mSessionId + " could not be restored"); 290 mSessionId = NO_SESSION; 291 } else { 292 if (sDebug) { 293 Log.d(TAG, "session " + mSessionId + " was restored"); 294 } 295 296 client.autofillCallbackResetableStateAvailable(); 297 } 298 } catch (RemoteException e) { 299 Log.e(TAG, "Could not figure out if there was an autofill session", e); 300 } 301 } 302 } 303 } 304 } 305 306 /** 307 * Called once the client becomes visible. 308 * 309 * @see AutofillClient#isVisibleForAutofill() 310 * 311 * {@hide} 312 */ 313 public void onVisibleForAutofill() { 314 synchronized (mLock) { 315 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) { 316 mTrackedViews.onVisibleForAutofillLocked(); 317 } 318 } 319 } 320 321 /** 322 * Save state before activity lifecycle 323 * 324 * @param outState Place to store the state 325 * 326 * {@hide} 327 */ 328 public void onSaveInstanceState(Bundle outState) { 329 if (!hasAutofillFeature()) { 330 return; 331 } 332 synchronized (mLock) { 333 if (mSessionId != NO_SESSION) { 334 outState.putInt(SESSION_ID_TAG, mSessionId); 335 } 336 337 if (mLastAutofilledData != null) { 338 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData); 339 } 340 } 341 } 342 343 /** 344 * Checks whether autofill is enabled for the current user. 345 * 346 * <p>Typically used to determine whether the option to explicitly request autofill should 347 * be offered - see {@link #requestAutofill(View)}. 348 * 349 * @return whether autofill is enabled for the current user. 350 */ 351 public boolean isEnabled() { 352 if (!hasAutofillFeature()) { 353 return false; 354 } 355 synchronized (mLock) { 356 ensureServiceClientAddedIfNeededLocked(); 357 return mEnabled; 358 } 359 } 360 361 /** 362 * Should always be called from {@link AutofillService#getFillEventHistory()}. 363 * 364 * @hide 365 */ 366 @Nullable public FillEventHistory getFillEventHistory() { 367 try { 368 return mService.getFillEventHistory(); 369 } catch (RemoteException e) { 370 e.rethrowFromSystemServer(); 371 return null; 372 } 373 } 374 375 /** 376 * Explicitly requests a new autofill context. 377 * 378 * <p>Normally, the autofill context is automatically started when autofillable views are 379 * focused, but this method should be used in the cases where it must be explicitly requested, 380 * like a view that provides a contextual menu allowing users to autofill the activity. 381 * 382 * @param view view requesting the new autofill context. 383 */ 384 public void requestAutofill(@NonNull View view) { 385 notifyViewEntered(view, FLAG_MANUAL_REQUEST); 386 } 387 388 /** 389 * Explicitly requests a new autofill context for virtual views. 390 * 391 * <p>Normally, the autofill context is automatically started when autofillable views are 392 * focused, but this method should be used in the cases where it must be explicitly requested, 393 * like a virtual view that provides a contextual menu allowing users to autofill the activity. 394 * 395 * @param view the {@link View} whose descendant is the virtual view. 396 * @param childId id identifying the virtual child inside the view. 397 * @param bounds child boundaries, relative to the top window. 398 */ 399 public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) { 400 notifyViewEntered(view, childId, bounds, FLAG_MANUAL_REQUEST); 401 } 402 403 /** 404 * Called when a {@link View} that supports autofill is entered. 405 * 406 * @param view {@link View} that was entered. 407 */ 408 public void notifyViewEntered(@NonNull View view) { 409 notifyViewEntered(view, 0); 410 } 411 412 private void notifyViewEntered(@NonNull View view, int flags) { 413 if (!hasAutofillFeature()) { 414 return; 415 } 416 AutofillCallback callback = null; 417 synchronized (mLock) { 418 ensureServiceClientAddedIfNeededLocked(); 419 420 if (!mEnabled) { 421 if (mCallback != null) { 422 callback = mCallback; 423 } 424 } else { 425 final AutofillId id = getAutofillId(view); 426 final AutofillValue value = view.getAutofillValue(); 427 428 if (mSessionId == NO_SESSION) { 429 // Starts new session. 430 startSessionLocked(id, null, value, flags); 431 } else { 432 // Update focus on existing session. 433 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); 434 } 435 } 436 } 437 438 if (callback != null) { 439 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 440 } 441 } 442 443 /** 444 * Called when a {@link View} that supports autofill is exited. 445 * 446 * @param view {@link View} that was exited. 447 */ 448 public void notifyViewExited(@NonNull View view) { 449 if (!hasAutofillFeature()) { 450 return; 451 } 452 synchronized (mLock) { 453 ensureServiceClientAddedIfNeededLocked(); 454 455 if (mEnabled && mSessionId != NO_SESSION) { 456 final AutofillId id = getAutofillId(view); 457 458 // Update focus on existing session. 459 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 460 } 461 } 462 } 463 464 /** 465 * Called when a {@link View view's} visibility changes. 466 * 467 * @param view {@link View} that was exited. 468 * @param isVisible visible if the view is visible in the view hierarchy. 469 * 470 * @hide 471 */ 472 public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { 473 synchronized (mLock) { 474 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) { 475 mTrackedViews.notifyViewVisibilityChange(view, isVisible); 476 } 477 } 478 } 479 480 /** 481 * Called when a virtual view that supports autofill is entered. 482 * 483 * @param view the {@link View} whose descendant is the virtual view. 484 * @param childId id identifying the virtual child inside the view. 485 * @param bounds child boundaries, relative to the top window. 486 */ 487 public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) { 488 notifyViewEntered(view, childId, bounds, 0); 489 } 490 491 private void notifyViewEntered(View view, int childId, Rect bounds, int flags) { 492 if (!hasAutofillFeature()) { 493 return; 494 } 495 AutofillCallback callback = null; 496 synchronized (mLock) { 497 ensureServiceClientAddedIfNeededLocked(); 498 499 if (!mEnabled) { 500 if (mCallback != null) { 501 callback = mCallback; 502 } 503 } else { 504 final AutofillId id = getAutofillId(view, childId); 505 506 if (mSessionId == NO_SESSION) { 507 // Starts new session. 508 startSessionLocked(id, bounds, null, flags); 509 } else { 510 // Update focus on existing session. 511 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); 512 } 513 } 514 } 515 516 if (callback != null) { 517 callback.onAutofillEvent(view, childId, 518 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 519 } 520 } 521 522 /** 523 * Called when a virtual view that supports autofill is exited. 524 * 525 * @param view the {@link View} whose descendant is the virtual view. 526 * @param childId id identifying the virtual child inside the view. 527 */ 528 public void notifyViewExited(@NonNull View view, int childId) { 529 if (!hasAutofillFeature()) { 530 return; 531 } 532 synchronized (mLock) { 533 ensureServiceClientAddedIfNeededLocked(); 534 535 if (mEnabled && mSessionId != NO_SESSION) { 536 final AutofillId id = getAutofillId(view, childId); 537 538 // Update focus on existing session. 539 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 540 } 541 } 542 } 543 544 /** 545 * Called to indicate the value of an autofillable {@link View} changed. 546 * 547 * @param view view whose value changed. 548 */ 549 public void notifyValueChanged(View view) { 550 if (!hasAutofillFeature()) { 551 return; 552 } 553 AutofillId id = null; 554 boolean valueWasRead = false; 555 AutofillValue value = null; 556 557 synchronized (mLock) { 558 // If the session is gone some fields might still be highlighted, hence we have to 559 // remove the isAutofilled property even if no sessions are active. 560 if (mLastAutofilledData == null) { 561 view.setAutofilled(false); 562 } else { 563 id = getAutofillId(view); 564 if (mLastAutofilledData.containsKey(id)) { 565 value = view.getAutofillValue(); 566 valueWasRead = true; 567 568 if (Objects.equals(mLastAutofilledData.get(id), value)) { 569 view.setAutofilled(true); 570 } else { 571 view.setAutofilled(false); 572 mLastAutofilledData.remove(id); 573 } 574 } else { 575 view.setAutofilled(false); 576 } 577 } 578 579 if (!mEnabled || mSessionId == NO_SESSION) { 580 return; 581 } 582 583 if (id == null) { 584 id = getAutofillId(view); 585 } 586 587 if (!valueWasRead) { 588 value = view.getAutofillValue(); 589 } 590 591 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 592 } 593 } 594 595 /** 596 * Called to indicate the value of an autofillable virtual {@link View} changed. 597 * 598 * @param view the {@link View} whose descendant is the virtual view. 599 * @param childId id identifying the virtual child inside the parent view. 600 * @param value new value of the child. 601 */ 602 public void notifyValueChanged(View view, int childId, AutofillValue value) { 603 if (!hasAutofillFeature()) { 604 return; 605 } 606 synchronized (mLock) { 607 if (!mEnabled || mSessionId == NO_SESSION) { 608 return; 609 } 610 611 final AutofillId id = getAutofillId(view, childId); 612 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 613 } 614 } 615 616 /** 617 * Called to indicate the current autofill context should be commited. 618 * 619 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should 620 * call this method after the form is submitted and another page is rendered. 621 */ 622 public void commit() { 623 if (!hasAutofillFeature()) { 624 return; 625 } 626 synchronized (mLock) { 627 if (!mEnabled && mSessionId == NO_SESSION) { 628 return; 629 } 630 631 finishSessionLocked(); 632 } 633 } 634 635 /** 636 * Called to indicate the current autofill context should be cancelled. 637 * 638 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should 639 * call this method if the user does not post the form but moves to another form in this page. 640 */ 641 public void cancel() { 642 if (!hasAutofillFeature()) { 643 return; 644 } 645 synchronized (mLock) { 646 if (!mEnabled && mSessionId == NO_SESSION) { 647 return; 648 } 649 650 cancelSessionLocked(); 651 } 652 } 653 654 /** @hide */ 655 public void disableOwnedAutofillServices() { 656 disableAutofillServices(); 657 } 658 659 /** 660 * If the app calling this API has enabled autofill services they 661 * will be disabled. 662 */ 663 public void disableAutofillServices() { 664 if (!hasAutofillFeature()) { 665 return; 666 } 667 try { 668 mService.disableOwnedAutofillServices(mContext.getUserId()); 669 } catch (RemoteException e) { 670 throw e.rethrowFromSystemServer(); 671 } 672 } 673 674 /** 675 * Returns {@code true} if the calling application provides a {@link AutofillService} that is 676 * enabled for the current user, or {@code false} otherwise. 677 */ 678 public boolean hasEnabledAutofillServices() { 679 if (mService == null) return false; 680 681 try { 682 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName()); 683 } catch (RemoteException e) { 684 throw e.rethrowFromSystemServer(); 685 } 686 } 687 688 /** 689 * Returns {@code true} if Autofill is supported for this user. 690 * 691 * <p>Autofill is typically supported, but it could be unsupported in cases like: 692 * <ol> 693 * <li>Low-end devices. 694 * <li>Device policy rules that forbid its usage. 695 * </ol> 696 */ 697 public boolean isAutofillSupported() { 698 if (mService == null) return false; 699 700 try { 701 return mService.isServiceSupported(mContext.getUserId()); 702 } catch (RemoteException e) { 703 throw e.rethrowFromSystemServer(); 704 } 705 } 706 707 private AutofillClient getClientLocked() { 708 if (mContext instanceof AutofillClient) { 709 return (AutofillClient) mContext; 710 } 711 return null; 712 } 713 714 /** @hide */ 715 public void onAuthenticationResult(int authenticationId, Intent data) { 716 if (!hasAutofillFeature()) { 717 return; 718 } 719 // TODO: the result code is being ignored, so this method is not reliably 720 // handling the cases where it's not RESULT_OK: it works fine if the service does not 721 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the 722 // service set the extra and returned RESULT_CANCELED... 723 724 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data); 725 726 synchronized (mLock) { 727 if (mSessionId == NO_SESSION || data == null) { 728 return; 729 } 730 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT); 731 final Bundle responseData = new Bundle(); 732 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); 733 try { 734 mService.setAuthenticationResult(responseData, mSessionId, authenticationId, 735 mContext.getUserId()); 736 } catch (RemoteException e) { 737 Log.e(TAG, "Error delivering authentication result", e); 738 } 739 } 740 } 741 742 private static AutofillId getAutofillId(View view) { 743 return new AutofillId(view.getAccessibilityViewId()); 744 } 745 746 private static AutofillId getAutofillId(View parent, int childId) { 747 return new AutofillId(parent.getAccessibilityViewId(), childId); 748 } 749 750 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, 751 @NonNull AutofillValue value, int flags) { 752 if (sVerbose) { 753 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value 754 + ", flags=" + flags); 755 } 756 757 try { 758 mSessionId = mService.startSession(mContext.getActivityToken(), 759 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 760 mCallback != null, flags, mContext.getOpPackageName()); 761 final AutofillClient client = getClientLocked(); 762 if (client != null) { 763 client.autofillCallbackResetableStateAvailable(); 764 } 765 } catch (RemoteException e) { 766 throw e.rethrowFromSystemServer(); 767 } 768 } 769 770 private void finishSessionLocked() { 771 if (sVerbose) Log.v(TAG, "finishSessionLocked()"); 772 773 try { 774 mService.finishSession(mSessionId, mContext.getUserId()); 775 } catch (RemoteException e) { 776 throw e.rethrowFromSystemServer(); 777 } 778 779 mTrackedViews = null; 780 mSessionId = NO_SESSION; 781 } 782 783 private void cancelSessionLocked() { 784 if (sVerbose) Log.v(TAG, "cancelSessionLocked()"); 785 786 try { 787 mService.cancelSession(mSessionId, mContext.getUserId()); 788 } catch (RemoteException e) { 789 throw e.rethrowFromSystemServer(); 790 } 791 792 resetSessionLocked(); 793 } 794 795 private void resetSessionLocked() { 796 mSessionId = NO_SESSION; 797 mTrackedViews = null; 798 } 799 800 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, 801 int flags) { 802 if (sVerbose && action != ACTION_VIEW_EXITED) { 803 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds 804 + ", value=" + value + ", action=" + action + ", flags=" + flags); 805 } 806 807 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0; 808 809 try { 810 if (restartIfNecessary) { 811 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(), 812 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 813 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action); 814 if (newId != mSessionId) { 815 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); 816 mSessionId = newId; 817 final AutofillClient client = getClientLocked(); 818 if (client != null) { 819 client.autofillCallbackResetableStateAvailable(); 820 } 821 } 822 } else { 823 mService.updateSession(mSessionId, id, bounds, value, action, flags, 824 mContext.getUserId()); 825 } 826 827 } catch (RemoteException e) { 828 throw e.rethrowFromSystemServer(); 829 } 830 } 831 832 private void ensureServiceClientAddedIfNeededLocked() { 833 if (getClientLocked() == null) { 834 return; 835 } 836 837 if (mServiceClient == null) { 838 mServiceClient = new AutofillManagerClient(this); 839 try { 840 final int flags = mService.addClient(mServiceClient, mContext.getUserId()); 841 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; 842 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0; 843 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0; 844 } catch (RemoteException e) { 845 throw e.rethrowFromSystemServer(); 846 } 847 } 848 } 849 850 /** 851 * Registers a {@link AutofillCallback} to receive autofill events. 852 * 853 * @param callback callback to receive events. 854 */ 855 public void registerCallback(@Nullable AutofillCallback callback) { 856 if (!hasAutofillFeature()) { 857 return; 858 } 859 synchronized (mLock) { 860 if (callback == null) return; 861 862 final boolean hadCallback = mCallback != null; 863 mCallback = callback; 864 865 if (!hadCallback) { 866 try { 867 mService.setHasCallback(mSessionId, mContext.getUserId(), true); 868 } catch (RemoteException e) { 869 throw e.rethrowFromSystemServer(); 870 } 871 } 872 } 873 } 874 875 /** 876 * Unregisters a {@link AutofillCallback} to receive autofill events. 877 * 878 * @param callback callback to stop receiving events. 879 */ 880 public void unregisterCallback(@Nullable AutofillCallback callback) { 881 if (!hasAutofillFeature()) { 882 return; 883 } 884 synchronized (mLock) { 885 if (callback == null || mCallback == null || callback != mCallback) return; 886 887 mCallback = null; 888 889 try { 890 mService.setHasCallback(mSessionId, mContext.getUserId(), false); 891 } catch (RemoteException e) { 892 throw e.rethrowFromSystemServer(); 893 } 894 } 895 } 896 897 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 898 Rect anchorBounds, IAutofillWindowPresenter presenter) { 899 final View anchor = findView(id); 900 if (anchor == null) { 901 return; 902 } 903 904 AutofillCallback callback = null; 905 synchronized (mLock) { 906 if (mSessionId == sessionId) { 907 AutofillClient client = getClientLocked(); 908 909 if (client != null) { 910 if (client.autofillCallbackRequestShowFillUi(anchor, width, height, 911 anchorBounds, presenter) && mCallback != null) { 912 callback = mCallback; 913 } 914 } 915 } 916 } 917 918 if (callback != null) { 919 if (id.isVirtual()) { 920 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 921 AutofillCallback.EVENT_INPUT_SHOWN); 922 } else { 923 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); 924 } 925 } 926 } 927 928 private void authenticate(int sessionId, int authenticationId, IntentSender intent, 929 Intent fillInIntent) { 930 synchronized (mLock) { 931 if (sessionId == mSessionId) { 932 AutofillClient client = getClientLocked(); 933 if (client != null) { 934 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent); 935 } 936 } 937 } 938 } 939 940 private void setState(boolean enabled, boolean resetSession, boolean resetClient) { 941 synchronized (mLock) { 942 mEnabled = enabled; 943 if (!mEnabled || resetSession) { 944 // Reset the session state 945 resetSessionLocked(); 946 } 947 if (resetClient) { 948 // Reset connection to system 949 mServiceClient = null; 950 } 951 } 952 } 953 954 /** 955 * Sets a view as autofilled if the current value is the {code targetValue}. 956 * 957 * @param view The view that is to be autofilled 958 * @param targetValue The value we want to fill into view 959 */ 960 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { 961 AutofillValue currentValue = view.getAutofillValue(); 962 if (Objects.equals(currentValue, targetValue)) { 963 synchronized (mLock) { 964 if (mLastAutofilledData == null) { 965 mLastAutofilledData = new ParcelableMap(1); 966 } 967 mLastAutofilledData.put(getAutofillId(view), targetValue); 968 } 969 view.setAutofilled(true); 970 } 971 } 972 973 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 974 synchronized (mLock) { 975 if (sessionId != mSessionId) { 976 return; 977 } 978 979 final AutofillClient client = getClientLocked(); 980 if (client == null) { 981 return; 982 } 983 984 final int itemCount = ids.size(); 985 int numApplied = 0; 986 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; 987 final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids)); 988 989 for (int i = 0; i < itemCount; i++) { 990 final AutofillId id = ids.get(i); 991 final AutofillValue value = values.get(i); 992 final int viewId = id.getViewId(); 993 final View view = views[i]; 994 if (view == null) { 995 Log.w(TAG, "autofill(): no View with id " + viewId); 996 continue; 997 } 998 if (id.isVirtual()) { 999 if (virtualValues == null) { 1000 // Most likely there will be just one view with virtual children. 1001 virtualValues = new ArrayMap<>(1); 1002 } 1003 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); 1004 if (valuesByParent == null) { 1005 // We don't know the size yet, but usually it will be just a few fields... 1006 valuesByParent = new SparseArray<>(5); 1007 virtualValues.put(view, valuesByParent); 1008 } 1009 valuesByParent.put(id.getVirtualChildId(), value); 1010 } else { 1011 // Mark the view as to be autofilled with 'value' 1012 if (mLastAutofilledData == null) { 1013 mLastAutofilledData = new ParcelableMap(itemCount - i); 1014 } 1015 mLastAutofilledData.put(id, value); 1016 1017 view.autofill(value); 1018 1019 // Set as autofilled if the values match now, e.g. when the value was updated 1020 // synchronously. 1021 // If autofill happens async, the view is set to autofilled in 1022 // notifyValueChanged. 1023 setAutofilledIfValuesIs(view, value); 1024 1025 numApplied++; 1026 } 1027 } 1028 1029 if (virtualValues != null) { 1030 for (int i = 0; i < virtualValues.size(); i++) { 1031 final View parent = virtualValues.keyAt(i); 1032 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); 1033 parent.autofill(childrenValues); 1034 numApplied += childrenValues.size(); 1035 } 1036 } 1037 1038 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); 1039 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); 1040 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, 1041 numApplied); 1042 mMetricsLogger.write(log); 1043 } 1044 } 1045 1046 /** 1047 * Set the tracked views. 1048 * 1049 * @param trackedIds The views to be tracked 1050 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible. 1051 */ 1052 private void setTrackedViews(int sessionId, List<AutofillId> trackedIds, 1053 boolean saveOnAllViewsInvisible) { 1054 synchronized (mLock) { 1055 if (mEnabled && mSessionId == sessionId) { 1056 if (saveOnAllViewsInvisible) { 1057 mTrackedViews = new TrackedViews(trackedIds); 1058 } else { 1059 mTrackedViews = null; 1060 } 1061 } 1062 } 1063 } 1064 1065 private void requestHideFillUi(int sessionId, AutofillId id) { 1066 final View anchor = findView(id); 1067 if (anchor == null) { 1068 return; 1069 } 1070 1071 AutofillCallback callback = null; 1072 synchronized (mLock) { 1073 // We do not check the session id for two reasons: 1074 // 1. If local and remote session id are off sync the UI would be stuck shown 1075 // 2. There is a race between the user state being destroyed due the fill 1076 // service being uninstalled and the UI being dismissed. 1077 AutofillClient client = getClientLocked(); 1078 if (client != null) { 1079 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) { 1080 callback = mCallback; 1081 } 1082 } 1083 } 1084 1085 if (callback != null) { 1086 if (id.isVirtual()) { 1087 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1088 AutofillCallback.EVENT_INPUT_HIDDEN); 1089 } else { 1090 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); 1091 } 1092 } 1093 } 1094 1095 private void notifyNoFillUi(int sessionId, AutofillId id) { 1096 final View anchor = findView(id); 1097 if (anchor == null) { 1098 return; 1099 } 1100 1101 AutofillCallback callback = null; 1102 synchronized (mLock) { 1103 if (mSessionId == sessionId && getClientLocked() != null) { 1104 callback = mCallback; 1105 } 1106 } 1107 1108 if (callback != null) { 1109 if (id.isVirtual()) { 1110 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1111 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1112 } else { 1113 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1114 } 1115 1116 } 1117 } 1118 1119 /** 1120 * Get an array of viewIds from a List of {@link AutofillId}. 1121 * 1122 * @param autofillIds The autofill ids to convert 1123 * 1124 * @return The array of viewIds. 1125 */ 1126 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) { 1127 final int numIds = autofillIds.size(); 1128 final int[] viewIds = new int[numIds]; 1129 for (int i = 0; i < numIds; i++) { 1130 viewIds[i] = autofillIds.get(i).getViewId(); 1131 } 1132 1133 return viewIds; 1134 } 1135 1136 /** 1137 * Find a single view by its id. 1138 * 1139 * @param autofillId The autofill id of the view 1140 * 1141 * @return The view or {@code null} if view was not found 1142 */ 1143 private View findView(@NonNull AutofillId autofillId) { 1144 final AutofillClient client = getClientLocked(); 1145 1146 if (client == null) { 1147 return null; 1148 } 1149 1150 return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0]; 1151 } 1152 1153 /** @hide */ 1154 public boolean hasAutofillFeature() { 1155 return mService != null; 1156 } 1157 1158 /** 1159 * View tracking information. Once all tracked views become invisible the session is finished. 1160 */ 1161 private class TrackedViews { 1162 /** Visible tracked views */ 1163 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; 1164 1165 /** Invisible tracked views */ 1166 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; 1167 1168 /** 1169 * Check if set is null or value is in set. 1170 * 1171 * @param set The set or null (== empty set) 1172 * @param value The value that might be in the set 1173 * 1174 * @return {@code true} iff set is not empty and value is in set 1175 */ 1176 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) { 1177 return set != null && set.contains(value); 1178 } 1179 1180 /** 1181 * Add a value to a set. If set is null, create a new set. 1182 * 1183 * @param set The set or null (== empty set) 1184 * @param valueToAdd The value to add 1185 * 1186 * @return The set including the new value. If set was {@code null}, a set containing only 1187 * the new value. 1188 */ 1189 @NonNull 1190 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) { 1191 if (set == null) { 1192 set = new ArraySet<>(1); 1193 } 1194 1195 set.add(valueToAdd); 1196 1197 return set; 1198 } 1199 1200 /** 1201 * Remove a value from a set. 1202 * 1203 * @param set The set or null (== empty set) 1204 * @param valueToRemove The value to remove 1205 * 1206 * @return The set without the removed value. {@code null} if set was null, or is empty 1207 * after removal. 1208 */ 1209 @Nullable 1210 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) { 1211 if (set == null) { 1212 return null; 1213 } 1214 1215 set.remove(valueToRemove); 1216 1217 if (set.isEmpty()) { 1218 return null; 1219 } 1220 1221 return set; 1222 } 1223 1224 /** 1225 * Set the tracked views. 1226 * 1227 * @param trackedIds The views to be tracked 1228 */ 1229 TrackedViews(List<AutofillId> trackedIds) { 1230 mVisibleTrackedIds = null; 1231 mInvisibleTrackedIds = null; 1232 1233 AutofillClient client = getClientLocked(); 1234 if (trackedIds != null && client != null) { 1235 final boolean[] isVisible; 1236 1237 if (client.isVisibleForAutofill()) { 1238 isVisible = client.getViewVisibility(getViewIds(trackedIds)); 1239 } else { 1240 // All false 1241 isVisible = new boolean[trackedIds.size()]; 1242 } 1243 1244 final int numIds = trackedIds.size(); 1245 for (int i = 0; i < numIds; i++) { 1246 final AutofillId id = trackedIds.get(i); 1247 1248 if (isVisible[i]) { 1249 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1250 } else { 1251 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1252 } 1253 } 1254 } 1255 1256 if (sVerbose) { 1257 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " 1258 + " mVisibleTrackedIds=" + mVisibleTrackedIds 1259 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); 1260 } 1261 1262 if (mVisibleTrackedIds == null) { 1263 finishSessionLocked(); 1264 } 1265 } 1266 1267 /** 1268 * Called when a {@link View view's} visibility changes. 1269 * 1270 * @param view {@link View} that was exited. 1271 * @param isVisible visible if the view is visible in the view hierarchy. 1272 */ 1273 void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { 1274 AutofillId id = getAutofillId(view); 1275 AutofillClient client = getClientLocked(); 1276 1277 if (sDebug) { 1278 Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible=" 1279 + isVisible); 1280 } 1281 1282 if (client != null && client.isVisibleForAutofill()) { 1283 if (isVisible) { 1284 if (isInSet(mInvisibleTrackedIds, id)) { 1285 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); 1286 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1287 } 1288 } else { 1289 if (isInSet(mVisibleTrackedIds, id)) { 1290 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); 1291 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1292 } 1293 } 1294 } 1295 1296 if (mVisibleTrackedIds == null) { 1297 finishSessionLocked(); 1298 } 1299 } 1300 1301 /** 1302 * Called once the client becomes visible. 1303 * 1304 * @see AutofillClient#isVisibleForAutofill() 1305 */ 1306 void onVisibleForAutofillLocked() { 1307 // The visibility of the views might have changed while the client was not be visible, 1308 // hence update the visibility state for all views. 1309 AutofillClient client = getClientLocked(); 1310 ArraySet<AutofillId> updatedVisibleTrackedIds = null; 1311 ArraySet<AutofillId> updatedInvisibleTrackedIds = null; 1312 if (client != null) { 1313 if (mInvisibleTrackedIds != null) { 1314 final ArrayList<AutofillId> orderedInvisibleIds = 1315 new ArrayList<>(mInvisibleTrackedIds); 1316 final boolean[] isVisible = client.getViewVisibility( 1317 getViewIds(orderedInvisibleIds)); 1318 1319 final int numInvisibleTrackedIds = orderedInvisibleIds.size(); 1320 for (int i = 0; i < numInvisibleTrackedIds; i++) { 1321 final AutofillId id = orderedInvisibleIds.get(i); 1322 if (isVisible[i]) { 1323 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1324 1325 if (sDebug) { 1326 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible"); 1327 } 1328 } else { 1329 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1330 } 1331 } 1332 } 1333 1334 if (mVisibleTrackedIds != null) { 1335 final ArrayList<AutofillId> orderedVisibleIds = 1336 new ArrayList<>(mVisibleTrackedIds); 1337 final boolean[] isVisible = client.getViewVisibility( 1338 getViewIds(orderedVisibleIds)); 1339 1340 final int numVisibleTrackedIds = orderedVisibleIds.size(); 1341 for (int i = 0; i < numVisibleTrackedIds; i++) { 1342 final AutofillId id = orderedVisibleIds.get(i); 1343 1344 if (isVisible[i]) { 1345 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1346 } else { 1347 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1348 1349 if (sDebug) { 1350 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible"); 1351 } 1352 } 1353 } 1354 } 1355 1356 mInvisibleTrackedIds = updatedInvisibleTrackedIds; 1357 mVisibleTrackedIds = updatedVisibleTrackedIds; 1358 } 1359 1360 if (mVisibleTrackedIds == null) { 1361 finishSessionLocked(); 1362 } 1363 } 1364 } 1365 1366 /** 1367 * Callback for auto-fill related events. 1368 * 1369 * <p>Typically used for applications that display their own "auto-complete" views, so they can 1370 * enable / disable such views when the auto-fill UI affordance is shown / hidden. 1371 */ 1372 public abstract static class AutofillCallback { 1373 1374 /** @hide */ 1375 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN}) 1376 @Retention(RetentionPolicy.SOURCE) 1377 public @interface AutofillEventType {} 1378 1379 /** 1380 * The auto-fill input UI affordance associated with the view was shown. 1381 * 1382 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it 1383 * should be hidden upon receiving this event. 1384 */ 1385 public static final int EVENT_INPUT_SHOWN = 1; 1386 1387 /** 1388 * The auto-fill input UI affordance associated with the view was hidden. 1389 * 1390 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a 1391 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now. 1392 */ 1393 public static final int EVENT_INPUT_HIDDEN = 2; 1394 1395 /** 1396 * The auto-fill input UI affordance associated with the view won't be shown because 1397 * autofill is not available. 1398 * 1399 * <p>If the view provides its own auto-complete UI affordance but was not displaying it 1400 * to avoid flickering, it could shown it upon receiving this event. 1401 */ 1402 public static final int EVENT_INPUT_UNAVAILABLE = 3; 1403 1404 /** 1405 * Called after a change in the autofill state associated with a view. 1406 * 1407 * @param view view associated with the change. 1408 * 1409 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 1410 */ 1411 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) { 1412 } 1413 1414 /** 1415 * Called after a change in the autofill state associated with a virtual view. 1416 * 1417 * @param view parent view associated with the change. 1418 * @param childId id identifying the virtual child inside the parent view. 1419 * 1420 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 1421 */ 1422 public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) { 1423 } 1424 } 1425 1426 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub { 1427 private final WeakReference<AutofillManager> mAfm; 1428 1429 AutofillManagerClient(AutofillManager autofillManager) { 1430 mAfm = new WeakReference<>(autofillManager); 1431 } 1432 1433 @Override 1434 public void setState(boolean enabled, boolean resetSession, boolean resetClient) { 1435 final AutofillManager afm = mAfm.get(); 1436 if (afm != null) { 1437 afm.mContext.getMainThreadHandler().post( 1438 () -> afm.setState(enabled, resetSession, resetClient)); 1439 } 1440 } 1441 1442 @Override 1443 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 1444 final AutofillManager afm = mAfm.get(); 1445 if (afm != null) { 1446 afm.mContext.getMainThreadHandler().post( 1447 () -> afm.autofill(sessionId, ids, values)); 1448 } 1449 } 1450 1451 @Override 1452 public void authenticate(int sessionId, int authenticationId, IntentSender intent, 1453 Intent fillInIntent) { 1454 final AutofillManager afm = mAfm.get(); 1455 if (afm != null) { 1456 afm.mContext.getMainThreadHandler().post( 1457 () -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent)); 1458 } 1459 } 1460 1461 @Override 1462 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 1463 Rect anchorBounds, IAutofillWindowPresenter presenter) { 1464 final AutofillManager afm = mAfm.get(); 1465 if (afm != null) { 1466 afm.mContext.getMainThreadHandler().post( 1467 () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds, 1468 presenter)); 1469 } 1470 } 1471 1472 @Override 1473 public void requestHideFillUi(int sessionId, AutofillId id) { 1474 final AutofillManager afm = mAfm.get(); 1475 if (afm != null) { 1476 afm.mContext.getMainThreadHandler().post( 1477 () -> afm.requestHideFillUi(sessionId, id)); 1478 } 1479 } 1480 1481 @Override 1482 public void notifyNoFillUi(int sessionId, AutofillId id) { 1483 final AutofillManager afm = mAfm.get(); 1484 if (afm != null) { 1485 afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id)); 1486 } 1487 } 1488 1489 @Override 1490 public void startIntentSender(IntentSender intentSender) { 1491 final AutofillManager afm = mAfm.get(); 1492 if (afm != null) { 1493 afm.mContext.getMainThreadHandler().post(() -> { 1494 try { 1495 afm.mContext.startIntentSender(intentSender, null, 0, 0, 0); 1496 } catch (IntentSender.SendIntentException e) { 1497 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e); 1498 } 1499 }); 1500 } 1501 } 1502 1503 @Override 1504 public void setTrackedViews(int sessionId, List<AutofillId> ids, 1505 boolean saveOnAllViewsInvisible) { 1506 final AutofillManager afm = mAfm.get(); 1507 if (afm != null) { 1508 afm.mContext.getMainThreadHandler().post( 1509 () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible) 1510 ); 1511 } 1512 } 1513 } 1514} 1515