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