AutofillManager.java revision 3103c63b2d47a51ad5a39309c91130aecc06665d
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.ComponentName; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentSender; 31import android.graphics.Rect; 32import android.metrics.LogMaker; 33import android.os.Bundle; 34import android.os.IBinder; 35import android.os.Parcelable; 36import android.os.RemoteException; 37import android.service.autofill.AutofillService; 38import android.service.autofill.FillEventHistory; 39import android.service.autofill.UserData; 40import android.util.ArrayMap; 41import android.util.ArraySet; 42import android.util.Log; 43import android.util.SparseArray; 44import android.view.View; 45 46import com.android.internal.annotations.GuardedBy; 47import com.android.internal.logging.MetricsLogger; 48import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 49import com.android.internal.util.Preconditions; 50 51import java.io.PrintWriter; 52import java.lang.annotation.Retention; 53import java.lang.annotation.RetentionPolicy; 54import java.lang.ref.WeakReference; 55import java.util.ArrayList; 56import java.util.List; 57import java.util.Objects; 58 59// TODO: use java.lang.ref.Cleaner once Android supports Java 9 60import sun.misc.Cleaner; 61 62/** 63 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the 64 * Autofill Framework lifecycle. 65 * 66 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an 67 * activity context; the autofill context is created when one of the following methods is called for 68 * the first time in an activity context, and the current user has an enabled autofill service: 69 * 70 * <ul> 71 * <li>{@link #notifyViewEntered(View)} 72 * <li>{@link #notifyViewEntered(View, int, Rect)} 73 * <li>{@link #requestAutofill(View)} 74 * </ul> 75 * 76 * <p>Tipically, the context is automatically created when the first view of the activity is 77 * focused because {@code View.onFocusChanged()} indirectly calls 78 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to 79 * explicitly create it (for example, a custom view developer could offer a contextual menu action 80 * in a text-field view to let users manually request autofill). 81 * 82 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure} 83 * that represents the view hierarchy by calling 84 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views 85 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in 86 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and 87 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in 88 * the hierarchy. 89 * 90 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which 91 * parses it looking for views that can be autofilled. If the service finds such views, it returns 92 * a data structure to the Android System containing the following optional info: 93 * 94 * <ul> 95 * <li>Datasets used to autofill subsets of views in the activity. 96 * <li>Id of views that the service can save their values for future autofilling. 97 * </ul> 98 * 99 * <p>When the service returns datasets, the Android System displays an autofill dataset picker 100 * UI associated with the view, when the view is focused on and is part of a dataset. 101 * The application can be notified when the UI is shown by registering an 102 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user 103 * selects a dataset from the UI, all views present in the dataset are autofilled, through 104 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}. 105 * 106 * <p>When the service returns ids of savable views, the Android System keeps track of changes 107 * made to these views, so they can be used to determine if the autofill save UI is shown later. 108 * 109 * <p>The context is then finished when one of the following occurs: 110 * 111 * <ul> 112 * <li>{@link #commit()} is called or all savable views are gone. 113 * <li>{@link #cancel()} is called. 114 * </ul> 115 * 116 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System 117 * shows an autofill save UI if the value of savable views have changed. If the user selects the 118 * option to Save, the current value of the views is then sent to the autofill service. 119 * 120 * <p>It is safe to call into its methods from any thread. 121 */ 122@SystemService(Context.AUTOFILL_MANAGER_SERVICE) 123public final class AutofillManager { 124 125 private static final String TAG = "AutofillManager"; 126 127 /** 128 * Intent extra: The assist structure which captures the filled screen. 129 * 130 * <p> 131 * Type: {@link android.app.assist.AssistStructure} 132 */ 133 public static final String EXTRA_ASSIST_STRUCTURE = 134 "android.view.autofill.extra.ASSIST_STRUCTURE"; 135 136 /** 137 * Intent extra: The result of an authentication operation. It is 138 * either a fully populated {@link android.service.autofill.FillResponse} 139 * or a fully populated {@link android.service.autofill.Dataset} if 140 * a response or a dataset is being authenticated respectively. 141 * 142 * <p> 143 * Type: {@link android.service.autofill.FillResponse} or a 144 * {@link android.service.autofill.Dataset} 145 */ 146 public static final String EXTRA_AUTHENTICATION_RESULT = 147 "android.view.autofill.extra.AUTHENTICATION_RESULT"; 148 149 /** 150 * Intent extra: The optional extras provided by the 151 * {@link android.service.autofill.AutofillService}. 152 * 153 * <p>For example, when the service responds to a {@link 154 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with 155 * a {@code FillResponse} that requires authentication, the Intent that launches the 156 * service authentication will contain the Bundle set by 157 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra. 158 * 159 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service 160 * can also add this bundle to the {@link Intent} set as the 161 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request, 162 * so the bundle can be recovered later on 163 * {@link android.service.autofill.SaveRequest#getClientState()}. 164 * 165 * <p> 166 * Type: {@link android.os.Bundle} 167 */ 168 public static final String EXTRA_CLIENT_STATE = 169 "android.view.autofill.extra.CLIENT_STATE"; 170 171 172 /** @hide */ 173 public static final String EXTRA_RESTORE_SESSION_TOKEN = 174 "android.view.autofill.extra.RESTORE_SESSION_TOKEN"; 175 176 private static final String SESSION_ID_TAG = "android:sessionId"; 177 private static final String STATE_TAG = "android:state"; 178 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; 179 180 181 /** @hide */ public static final int ACTION_START_SESSION = 1; 182 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2; 183 /** @hide */ public static final int ACTION_VIEW_EXITED = 3; 184 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4; 185 186 187 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1; 188 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; 189 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; 190 191 /** Which bits in an authentication id are used for the dataset id */ 192 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF; 193 /** How many bits in an authentication id are used for the dataset id */ 194 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16; 195 /** @hide The index for an undefined data set */ 196 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF; 197 198 /** 199 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI. 200 * 201 * @hide 202 */ 203 public static final int PENDING_UI_OPERATION_CANCEL = 1; 204 205 /** 206 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI. 207 * 208 * @hide 209 */ 210 public static final int PENDING_UI_OPERATION_RESTORE = 2; 211 212 /** 213 * Initial state of the autofill context, set when there is no session (i.e., when 214 * {@link #mSessionId} is {@link #NO_SESSION}). 215 * 216 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to 217 * the server. 218 * 219 * @hide 220 */ 221 public static final int STATE_UNKNOWN = 0; 222 223 /** 224 * State where the autofill context hasn't been {@link #commit() finished} nor 225 * {@link #cancel() canceled} yet. 226 * 227 * @hide 228 */ 229 public static final int STATE_ACTIVE = 1; 230 231 /** 232 * State where the autofill context was finished by the server because the autofill 233 * service could not autofill the activity. 234 * 235 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored, 236 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}). 237 * 238 * @hide 239 */ 240 public static final int STATE_FINISHED = 2; 241 242 /** 243 * State where the autofill context has been {@link #commit() finished} but the server still has 244 * a session because the Save UI hasn't been dismissed yet. 245 * 246 * @hide 247 */ 248 public static final int STATE_SHOWING_SAVE_UI = 3; 249 250 /** 251 * State where the autofill is disabled because the service cannot autofill the activity at all. 252 * 253 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)} 254 * (and {@link #requestAutofill(View, int, Rect)}). 255 * 256 * @hide 257 */ 258 public static final int STATE_DISABLED_BY_SERVICE = 4; 259 260 /** 261 * Makes an authentication id from a request id and a dataset id. 262 * 263 * @param requestId The request id. 264 * @param datasetId The dataset id. 265 * @return The authentication id. 266 * @hide 267 */ 268 public static int makeAuthenticationId(int requestId, int datasetId) { 269 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT) 270 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK); 271 } 272 273 /** 274 * Gets the request id from an authentication id. 275 * 276 * @param authRequestId The authentication id. 277 * @return The request id. 278 * @hide 279 */ 280 public static int getRequestIdFromAuthenticationId(int authRequestId) { 281 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT); 282 } 283 284 /** 285 * Gets the dataset id from an authentication id. 286 * 287 * @param authRequestId The authentication id. 288 * @return The dataset id. 289 * @hide 290 */ 291 public static int getDatasetIdFromAuthenticationId(int authRequestId) { 292 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK); 293 } 294 295 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 296 297 /** 298 * There is currently no session running. 299 * {@hide} 300 */ 301 public static final int NO_SESSION = Integer.MIN_VALUE; 302 303 private final IAutoFillManager mService; 304 305 private final Object mLock = new Object(); 306 307 @GuardedBy("mLock") 308 private IAutoFillManagerClient mServiceClient; 309 310 @GuardedBy("mLock") 311 private Cleaner mServiceClientCleaner; 312 313 @GuardedBy("mLock") 314 private AutofillCallback mCallback; 315 316 private final Context mContext; 317 318 @GuardedBy("mLock") 319 private int mSessionId = NO_SESSION; 320 321 @GuardedBy("mLock") 322 private int mState = STATE_UNKNOWN; 323 324 @GuardedBy("mLock") 325 private boolean mEnabled; 326 327 /** If a view changes to this mapping the autofill operation was successful */ 328 @GuardedBy("mLock") 329 @Nullable private ParcelableMap mLastAutofilledData; 330 331 /** If view tracking is enabled, contains the tracking state */ 332 @GuardedBy("mLock") 333 @Nullable private TrackedViews mTrackedViews; 334 335 /** Views that are only tracked because they are fillable and could be anchoring the UI. */ 336 @GuardedBy("mLock") 337 @Nullable private ArraySet<AutofillId> mFillableIds; 338 339 /** If set, session is commited when the field is clicked. */ 340 @GuardedBy("mLock") 341 @Nullable private AutofillId mSaveTriggerId; 342 343 /** If set, session is commited when the activity is finished; otherwise session is canceled. */ 344 @GuardedBy("mLock") 345 private boolean mSaveOnFinish; 346 347 /** @hide */ 348 public interface AutofillClient { 349 /** 350 * Asks the client to start an authentication flow. 351 * 352 * @param authenticationId A unique id of the authentication operation. 353 * @param intent The authentication intent. 354 * @param fillInIntent The authentication fill-in intent. 355 */ 356 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent, 357 Intent fillInIntent); 358 359 /** 360 * Tells the client this manager has state to be reset. 361 */ 362 void autofillCallbackResetableStateAvailable(); 363 364 /** 365 * Request showing the autofill UI. 366 * 367 * @param anchor The real view the UI needs to anchor to. 368 * @param width The width of the fill UI content. 369 * @param height The height of the fill UI content. 370 * @param virtualBounds The bounds of the virtual decendant of the anchor. 371 * @param presenter The presenter that controls the fill UI window. 372 * @return Whether the UI was shown. 373 */ 374 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, 375 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); 376 377 /** 378 * Request hiding the autofill UI. 379 * 380 * @return Whether the UI was hidden. 381 */ 382 boolean autofillCallbackRequestHideFillUi(); 383 384 /** 385 * Checks if views are currently attached and visible. 386 * 387 * @return And array with {@code true} iff the view is attached or visible 388 */ 389 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId); 390 391 /** 392 * Checks is the client is currently visible as understood by autofill. 393 * 394 * @return {@code true} if the client is currently visible 395 */ 396 boolean isVisibleForAutofill(); 397 398 /** 399 * Finds views by traversing the hierarchies of the client. 400 * 401 * @param viewIds The autofill ids of the views to find 402 * 403 * @return And array containing the views (empty if no views found). 404 */ 405 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds); 406 407 /** 408 * Finds a view by traversing the hierarchies of the client. 409 * 410 * @param viewId The autofill id of the views to find 411 * 412 * @return The view, or {@code null} if not found 413 */ 414 @Nullable View findViewByAutofillIdTraversal(int viewId); 415 416 /** 417 * Runs the specified action on the UI thread. 418 */ 419 void runOnUiThread(Runnable action); 420 421 /** 422 * Gets the complete component name of this client. 423 */ 424 ComponentName getComponentName(); 425 } 426 427 /** 428 * @hide 429 */ 430 public AutofillManager(Context context, IAutoFillManager service) { 431 mContext = Preconditions.checkNotNull(context, "context cannot be null"); 432 mService = service; 433 } 434 435 /** 436 * Restore state after activity lifecycle 437 * 438 * @param savedInstanceState The state to be restored 439 * 440 * {@hide} 441 */ 442 public void onCreate(Bundle savedInstanceState) { 443 if (!hasAutofillFeature()) { 444 return; 445 } 446 synchronized (mLock) { 447 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG); 448 449 if (isActiveLocked()) { 450 Log.w(TAG, "New session was started before onCreate()"); 451 return; 452 } 453 454 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION); 455 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN); 456 457 if (mSessionId != NO_SESSION) { 458 ensureServiceClientAddedIfNeededLocked(); 459 460 final AutofillClient client = getClient(); 461 if (client != null) { 462 try { 463 final boolean sessionWasRestored = mService.restoreSession(mSessionId, 464 mContext.getActivityToken(), mServiceClient.asBinder()); 465 466 if (!sessionWasRestored) { 467 Log.w(TAG, "Session " + mSessionId + " could not be restored"); 468 mSessionId = NO_SESSION; 469 mState = STATE_UNKNOWN; 470 } else { 471 if (sDebug) { 472 Log.d(TAG, "session " + mSessionId + " was restored"); 473 } 474 475 client.autofillCallbackResetableStateAvailable(); 476 } 477 } catch (RemoteException e) { 478 Log.e(TAG, "Could not figure out if there was an autofill session", e); 479 } 480 } 481 } 482 } 483 } 484 485 /** 486 * Called once the client becomes visible. 487 * 488 * @see AutofillClient#isVisibleForAutofill() 489 * 490 * {@hide} 491 */ 492 public void onVisibleForAutofill() { 493 synchronized (mLock) { 494 if (mEnabled && isActiveLocked() && mTrackedViews != null) { 495 mTrackedViews.onVisibleForAutofillLocked(); 496 } 497 } 498 } 499 500 /** 501 * Save state before activity lifecycle 502 * 503 * @param outState Place to store the state 504 * 505 * {@hide} 506 */ 507 public void onSaveInstanceState(Bundle outState) { 508 if (!hasAutofillFeature()) { 509 return; 510 } 511 synchronized (mLock) { 512 if (mSessionId != NO_SESSION) { 513 outState.putInt(SESSION_ID_TAG, mSessionId); 514 } 515 if (mState != STATE_UNKNOWN) { 516 outState.putInt(STATE_TAG, mState); 517 } 518 if (mLastAutofilledData != null) { 519 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData); 520 } 521 } 522 } 523 524 /** 525 * Checks whether autofill is enabled for the current user. 526 * 527 * <p>Typically used to determine whether the option to explicitly request autofill should 528 * be offered - see {@link #requestAutofill(View)}. 529 * 530 * @return whether autofill is enabled for the current user. 531 */ 532 public boolean isEnabled() { 533 if (!hasAutofillFeature()) { 534 return false; 535 } 536 synchronized (mLock) { 537 if (isDisabledByServiceLocked()) { 538 return false; 539 } 540 ensureServiceClientAddedIfNeededLocked(); 541 return mEnabled; 542 } 543 } 544 545 /** 546 * Should always be called from {@link AutofillService#getFillEventHistory()}. 547 * 548 * @hide 549 */ 550 @Nullable public FillEventHistory getFillEventHistory() { 551 try { 552 return mService.getFillEventHistory(); 553 } catch (RemoteException e) { 554 e.rethrowFromSystemServer(); 555 return null; 556 } 557 } 558 559 /** 560 * Explicitly requests a new autofill context. 561 * 562 * <p>Normally, the autofill context is automatically started if necessary when 563 * {@link #notifyViewEntered(View)} is called, but this method should be used in the 564 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL 565 * option on its contextual overflow menu, and the user selects it. 566 * 567 * @param view view requesting the new autofill context. 568 */ 569 public void requestAutofill(@NonNull View view) { 570 notifyViewEntered(view, FLAG_MANUAL_REQUEST); 571 } 572 573 /** 574 * Explicitly requests a new autofill context for virtual views. 575 * 576 * <p>Normally, the autofill context is automatically started if necessary when 577 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the 578 * cases where it must be explicitly started. For example, when the virtual view offers an 579 * AUTOFILL option on its contextual overflow menu, and the user selects it. 580 * 581 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 582 * parent view uses {@code bounds} to draw the virtual view inside its Canvas, 583 * the absolute bounds could be calculated by: 584 * 585 * <pre class="prettyprint"> 586 * int offset[] = new int[2]; 587 * getLocationOnScreen(offset); 588 * Rect absBounds = new Rect(bounds.left + offset[0], 589 * bounds.top + offset[1], 590 * bounds.right + offset[0], bounds.bottom + offset[1]); 591 * </pre> 592 * 593 * @param view the virtual view parent. 594 * @param virtualId id identifying the virtual child inside the parent view. 595 * @param absBounds absolute boundaries of the virtual view in the screen. 596 */ 597 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 598 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST); 599 } 600 601 /** 602 * Called when a {@link View} that supports autofill is entered. 603 * 604 * @param view {@link View} that was entered. 605 */ 606 public void notifyViewEntered(@NonNull View view) { 607 notifyViewEntered(view, 0); 608 } 609 610 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) { 611 if (isDisabledByServiceLocked()) { 612 if (sVerbose) { 613 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view 614 + ") on state " + getStateAsStringLocked()); 615 } 616 return true; 617 } 618 if (sVerbose && isFinishedLocked()) { 619 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view 620 + ") on state " + getStateAsStringLocked()); 621 } 622 return false; 623 } 624 625 private void notifyViewEntered(@NonNull View view, int flags) { 626 if (!hasAutofillFeature()) { 627 return; 628 } 629 AutofillCallback callback = null; 630 synchronized (mLock) { 631 if (shouldIgnoreViewEnteredLocked(view, flags)) return; 632 633 ensureServiceClientAddedIfNeededLocked(); 634 635 if (!mEnabled) { 636 if (mCallback != null) { 637 callback = mCallback; 638 } 639 } else { 640 final AutofillId id = getAutofillId(view); 641 final AutofillValue value = view.getAutofillValue(); 642 643 if (!isActiveLocked()) { 644 // Starts new session. 645 startSessionLocked(id, null, value, flags); 646 } else { 647 // Update focus on existing session. 648 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); 649 } 650 } 651 } 652 653 if (callback != null) { 654 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 655 } 656 } 657 658 /** 659 * Called when a {@link View} that supports autofill is exited. 660 * 661 * @param view {@link View} that was exited. 662 */ 663 public void notifyViewExited(@NonNull View view) { 664 if (!hasAutofillFeature()) { 665 return; 666 } 667 synchronized (mLock) { 668 ensureServiceClientAddedIfNeededLocked(); 669 670 if (mEnabled && isActiveLocked()) { 671 final AutofillId id = getAutofillId(view); 672 673 // Update focus on existing session. 674 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 675 } 676 } 677 } 678 679 /** 680 * Called when a {@link View view's} visibility changed. 681 * 682 * @param view {@link View} that was exited. 683 * @param isVisible visible if the view is visible in the view hierarchy. 684 */ 685 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) { 686 notifyViewVisibilityChangedInternal(view, 0, isVisible, false); 687 } 688 689 /** 690 * Called when a virtual view's visibility changed. 691 * 692 * @param view {@link View} that was exited. 693 * @param virtualId id identifying the virtual child inside the parent view. 694 * @param isVisible visible if the view is visible in the view hierarchy. 695 */ 696 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) { 697 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true); 698 } 699 700 /** 701 * Called when a view/virtual view's visibility changed. 702 * 703 * @param view {@link View} that was exited. 704 * @param virtualId id identifying the virtual child inside the parent view. 705 * @param isVisible visible if the view is visible in the view hierarchy. 706 * @param virtual Whether the view is virtual. 707 */ 708 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId, 709 boolean isVisible, boolean virtual) { 710 synchronized (mLock) { 711 if (mEnabled && isActiveLocked()) { 712 final AutofillId id = virtual ? getAutofillId(view, virtualId) 713 : view.getAutofillId(); 714 if (!isVisible && mFillableIds != null) { 715 if (mFillableIds.contains(id)) { 716 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible"); 717 requestHideFillUi(id, view); 718 } 719 } 720 if (mTrackedViews != null) { 721 mTrackedViews.notifyViewVisibilityChanged(id, isVisible); 722 } 723 } 724 } 725 } 726 727 /** 728 * Called when a virtual view that supports autofill is entered. 729 * 730 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 731 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas, 732 * the absolute bounds could be calculated by: 733 * 734 * <pre class="prettyprint"> 735 * int offset[] = new int[2]; 736 * getLocationOnScreen(offset); 737 * Rect absBounds = new Rect(bounds.left + offset[0], 738 * bounds.top + offset[1], 739 * bounds.right + offset[0], bounds.bottom + offset[1]); 740 * </pre> 741 * 742 * @param view the virtual view parent. 743 * @param virtualId id identifying the virtual child inside the parent view. 744 * @param absBounds absolute boundaries of the virtual view in the screen. 745 */ 746 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 747 notifyViewEntered(view, virtualId, absBounds, 0); 748 } 749 750 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) { 751 if (!hasAutofillFeature()) { 752 return; 753 } 754 AutofillCallback callback = null; 755 synchronized (mLock) { 756 if (shouldIgnoreViewEnteredLocked(view, flags)) return; 757 758 ensureServiceClientAddedIfNeededLocked(); 759 760 if (!mEnabled) { 761 if (mCallback != null) { 762 callback = mCallback; 763 } 764 } else { 765 final AutofillId id = getAutofillId(view, virtualId); 766 767 if (!isActiveLocked()) { 768 // Starts new session. 769 startSessionLocked(id, bounds, null, flags); 770 } else { 771 // Update focus on existing session. 772 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); 773 } 774 } 775 } 776 777 if (callback != null) { 778 callback.onAutofillEvent(view, virtualId, 779 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 780 } 781 } 782 783 /** 784 * Called when a virtual view that supports autofill is exited. 785 * 786 * @param view the virtual view parent. 787 * @param virtualId id identifying the virtual child inside the parent view. 788 */ 789 public void notifyViewExited(@NonNull View view, int virtualId) { 790 if (!hasAutofillFeature()) { 791 return; 792 } 793 synchronized (mLock) { 794 ensureServiceClientAddedIfNeededLocked(); 795 796 if (mEnabled && isActiveLocked()) { 797 final AutofillId id = getAutofillId(view, virtualId); 798 799 // Update focus on existing session. 800 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 801 } 802 } 803 } 804 805 /** 806 * Called to indicate the value of an autofillable {@link View} changed. 807 * 808 * @param view view whose value changed. 809 */ 810 public void notifyValueChanged(View view) { 811 if (!hasAutofillFeature()) { 812 return; 813 } 814 AutofillId id = null; 815 boolean valueWasRead = false; 816 AutofillValue value = null; 817 818 synchronized (mLock) { 819 // If the session is gone some fields might still be highlighted, hence we have to 820 // remove the isAutofilled property even if no sessions are active. 821 if (mLastAutofilledData == null) { 822 view.setAutofilled(false); 823 } else { 824 id = getAutofillId(view); 825 if (mLastAutofilledData.containsKey(id)) { 826 value = view.getAutofillValue(); 827 valueWasRead = true; 828 829 if (Objects.equals(mLastAutofilledData.get(id), value)) { 830 view.setAutofilled(true); 831 } else { 832 view.setAutofilled(false); 833 mLastAutofilledData.remove(id); 834 } 835 } else { 836 view.setAutofilled(false); 837 } 838 } 839 840 if (!mEnabled || !isActiveLocked()) { 841 if (sVerbose && mEnabled) { 842 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state " 843 + getStateAsStringLocked()); 844 } 845 return; 846 } 847 848 if (id == null) { 849 id = getAutofillId(view); 850 } 851 852 if (!valueWasRead) { 853 value = view.getAutofillValue(); 854 } 855 856 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 857 } 858 } 859 860 /** 861 * Called to indicate the value of an autofillable virtual view has changed. 862 * 863 * @param view the virtual view parent. 864 * @param virtualId id identifying the virtual child inside the parent view. 865 * @param value new value of the child. 866 */ 867 public void notifyValueChanged(View view, int virtualId, AutofillValue value) { 868 if (!hasAutofillFeature()) { 869 return; 870 } 871 synchronized (mLock) { 872 if (!mEnabled || !isActiveLocked()) { 873 return; 874 } 875 876 final AutofillId id = getAutofillId(view, virtualId); 877 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 878 } 879 } 880 881 882 /** 883 * Called when a {@link View} is clicked. Currently only used by views that should trigger save. 884 * 885 * @hide 886 */ 887 public void notifyViewClicked(View view) { 888 final AutofillId id = view.getAutofillId(); 889 890 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId); 891 892 synchronized (mLock) { 893 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) { 894 if (sDebug) Log.d(TAG, "triggering commit by click of " + id); 895 commitLocked(); 896 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED, 897 mContext.getPackageName()); 898 } 899 } 900 } 901 902 /** 903 * Called by {@link android.app.Activity} to commit or cancel the session on finish. 904 * 905 * @hide 906 */ 907 public void onActivityFinished() { 908 if (!hasAutofillFeature()) { 909 return; 910 } 911 synchronized (mLock) { 912 if (mSaveOnFinish) { 913 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service"); 914 commitLocked(); 915 } else { 916 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service"); 917 cancelLocked(); 918 } 919 } 920 } 921 922 /** 923 * Called to indicate the current autofill context should be commited. 924 * 925 * <p>This method is typically called by {@link View Views} that manage virtual views; for 926 * example, when the view is rendering an {@code HTML} page with a form and virtual views 927 * that represent the HTML elements, it should call this method after the form is submitted and 928 * another page is rendered. 929 * 930 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle 931 * methods such as {@link android.app.Activity#finish()}. 932 */ 933 public void commit() { 934 if (!hasAutofillFeature()) { 935 return; 936 } 937 synchronized (mLock) { 938 commitLocked(); 939 } 940 } 941 942 private void commitLocked() { 943 if (!mEnabled && !isActiveLocked()) { 944 return; 945 } 946 finishSessionLocked(); 947 } 948 949 /** 950 * Called to indicate the current autofill context should be cancelled. 951 * 952 * <p>This method is typically called by {@link View Views} that manage virtual views; for 953 * example, when the view is rendering an {@code HTML} page with a form and virtual views 954 * that represent the HTML elements, it should call this method if the user does not post the 955 * form but moves to another form in this page. 956 * 957 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle 958 * methods such as {@link android.app.Activity#finish()}. 959 */ 960 public void cancel() { 961 if (sVerbose) Log.v(TAG, "cancel() called by app"); 962 if (!hasAutofillFeature()) { 963 return; 964 } 965 synchronized (mLock) { 966 cancelLocked(); 967 } 968 } 969 970 private void cancelLocked() { 971 if (!mEnabled && !isActiveLocked()) { 972 return; 973 } 974 cancelSessionLocked(); 975 } 976 977 /** @hide */ 978 public void disableOwnedAutofillServices() { 979 disableAutofillServices(); 980 } 981 982 /** 983 * If the app calling this API has enabled autofill services they 984 * will be disabled. 985 */ 986 public void disableAutofillServices() { 987 if (!hasAutofillFeature()) { 988 return; 989 } 990 try { 991 mService.disableOwnedAutofillServices(mContext.getUserId()); 992 } catch (RemoteException e) { 993 throw e.rethrowFromSystemServer(); 994 } 995 } 996 997 /** 998 * Returns {@code true} if the calling application provides a {@link AutofillService} that is 999 * enabled for the current user, or {@code false} otherwise. 1000 */ 1001 public boolean hasEnabledAutofillServices() { 1002 if (mService == null) return false; 1003 1004 try { 1005 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName()); 1006 } catch (RemoteException e) { 1007 throw e.rethrowFromSystemServer(); 1008 } 1009 } 1010 1011 /** 1012 * Gets the user data used for 1013 * <a href="AutofillService.html#FieldClassification">field classification</a>. 1014 * 1015 * <p><b>Note:</b> This method should only be called by an app providing an autofill service. 1016 * 1017 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was 1018 * reset or if the caller currently does not have an enabled autofill service for the user. 1019 */ 1020 @Nullable public UserData getUserData() { 1021 try { 1022 return mService.getUserData(); 1023 } catch (RemoteException e) { 1024 e.rethrowFromSystemServer(); 1025 return null; 1026 } 1027 } 1028 1029 /** 1030 * Sets the user data used for 1031 * <a href="AutofillService.html#FieldClassification">field classification</a> 1032 * 1033 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1034 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1035 * the user. 1036 */ 1037 public void setUserData(@Nullable UserData userData) { 1038 try { 1039 mService.setUserData(userData); 1040 } catch (RemoteException e) { 1041 e.rethrowFromSystemServer(); 1042 } 1043 } 1044 1045 /** 1046 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is 1047 * enabled. 1048 * 1049 * <p>As field classification is an expensive operation, it could be disabled, either 1050 * temporarily (for example, because the service exceeded a rate-limit threshold) or 1051 * permanently (for example, because the device is a low-level device). 1052 * 1053 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1054 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1055 * the user. 1056 */ 1057 public boolean isFieldClassificationEnabled() { 1058 try { 1059 return mService.isFieldClassificationEnabled(); 1060 } catch (RemoteException e) { 1061 e.rethrowFromSystemServer(); 1062 return false; 1063 } 1064 } 1065 1066 /** 1067 * Returns {@code true} if autofill is supported by the current device and 1068 * is supported for this user. 1069 * 1070 * <p>Autofill is typically supported, but it could be unsupported in cases like: 1071 * <ol> 1072 * <li>Low-end devices. 1073 * <li>Device policy rules that forbid its usage. 1074 * </ol> 1075 */ 1076 public boolean isAutofillSupported() { 1077 if (mService == null) return false; 1078 1079 try { 1080 return mService.isServiceSupported(mContext.getUserId()); 1081 } catch (RemoteException e) { 1082 throw e.rethrowFromSystemServer(); 1083 } 1084 } 1085 1086 // Note: don't need to use locked suffix because mContext is final. 1087 private AutofillClient getClient() { 1088 final AutofillClient client = mContext.getAutofillClient(); 1089 if (client == null && sDebug) { 1090 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context " 1091 + mContext); 1092 } 1093 return client; 1094 } 1095 1096 /** @hide */ 1097 public void onAuthenticationResult(int authenticationId, Intent data) { 1098 if (!hasAutofillFeature()) { 1099 return; 1100 } 1101 // TODO: the result code is being ignored, so this method is not reliably 1102 // handling the cases where it's not RESULT_OK: it works fine if the service does not 1103 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the 1104 // service set the extra and returned RESULT_CANCELED... 1105 1106 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data); 1107 1108 synchronized (mLock) { 1109 if (!isActiveLocked() || data == null) { 1110 return; 1111 } 1112 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT); 1113 final Bundle responseData = new Bundle(); 1114 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); 1115 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE); 1116 if (newClientState != null) { 1117 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState); 1118 } 1119 try { 1120 mService.setAuthenticationResult(responseData, mSessionId, authenticationId, 1121 mContext.getUserId()); 1122 } catch (RemoteException e) { 1123 Log.e(TAG, "Error delivering authentication result", e); 1124 } 1125 } 1126 } 1127 1128 private static AutofillId getAutofillId(View view) { 1129 return new AutofillId(view.getAutofillViewId()); 1130 } 1131 1132 private static AutofillId getAutofillId(View parent, int virtualId) { 1133 return new AutofillId(parent.getAutofillViewId(), virtualId); 1134 } 1135 1136 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, 1137 @NonNull AutofillValue value, int flags) { 1138 if (sVerbose) { 1139 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value 1140 + ", flags=" + flags + ", state=" + getStateAsStringLocked()); 1141 } 1142 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) { 1143 if (sVerbose) { 1144 Log.v(TAG, "not automatically starting session for " + id 1145 + " on state " + getStateAsStringLocked() + " and flags " + flags); 1146 } 1147 return; 1148 } 1149 try { 1150 final AutofillClient client = getClient(); 1151 if (client == null) return; // NOTE: getClient() already logd it.. 1152 1153 mSessionId = mService.startSession(mContext.getActivityToken(), 1154 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 1155 mCallback != null, flags, client.getComponentName()); 1156 if (mSessionId != NO_SESSION) { 1157 mState = STATE_ACTIVE; 1158 } 1159 client.autofillCallbackResetableStateAvailable(); 1160 } catch (RemoteException e) { 1161 throw e.rethrowFromSystemServer(); 1162 } 1163 } 1164 1165 private void finishSessionLocked() { 1166 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked()); 1167 1168 if (!isActiveLocked()) return; 1169 1170 try { 1171 mService.finishSession(mSessionId, mContext.getUserId()); 1172 } catch (RemoteException e) { 1173 throw e.rethrowFromSystemServer(); 1174 } 1175 1176 resetSessionLocked(); 1177 } 1178 1179 private void cancelSessionLocked() { 1180 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked()); 1181 1182 if (!isActiveLocked()) return; 1183 1184 try { 1185 mService.cancelSession(mSessionId, mContext.getUserId()); 1186 } catch (RemoteException e) { 1187 throw e.rethrowFromSystemServer(); 1188 } 1189 1190 resetSessionLocked(); 1191 } 1192 1193 private void resetSessionLocked() { 1194 mSessionId = NO_SESSION; 1195 mState = STATE_UNKNOWN; 1196 mTrackedViews = null; 1197 mFillableIds = null; 1198 mSaveTriggerId = null; 1199 } 1200 1201 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, 1202 int flags) { 1203 if (sVerbose && action != ACTION_VIEW_EXITED) { 1204 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds 1205 + ", value=" + value + ", action=" + action + ", flags=" + flags); 1206 } 1207 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0; 1208 1209 try { 1210 if (restartIfNecessary) { 1211 final AutofillClient client = getClient(); 1212 if (client == null) return; // NOTE: getClient() already logd it.. 1213 1214 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(), 1215 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 1216 mCallback != null, flags, client.getComponentName(), mSessionId, action); 1217 if (newId != mSessionId) { 1218 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); 1219 mSessionId = newId; 1220 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE; 1221 client.autofillCallbackResetableStateAvailable(); 1222 } 1223 } else { 1224 mService.updateSession(mSessionId, id, bounds, value, action, flags, 1225 mContext.getUserId()); 1226 } 1227 1228 } catch (RemoteException e) { 1229 throw e.rethrowFromSystemServer(); 1230 } 1231 } 1232 1233 private void ensureServiceClientAddedIfNeededLocked() { 1234 if (getClient() == null) { 1235 return; 1236 } 1237 1238 if (mServiceClient == null) { 1239 mServiceClient = new AutofillManagerClient(this); 1240 try { 1241 final int userId = mContext.getUserId(); 1242 final int flags = mService.addClient(mServiceClient, userId); 1243 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; 1244 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0; 1245 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0; 1246 final IAutoFillManager service = mService; 1247 final IAutoFillManagerClient serviceClient = mServiceClient; 1248 mServiceClientCleaner = Cleaner.create(this, () -> { 1249 try { 1250 service.removeClient(serviceClient, userId); 1251 } catch (RemoteException e) { 1252 } 1253 }); 1254 } catch (RemoteException e) { 1255 throw e.rethrowFromSystemServer(); 1256 } 1257 } 1258 } 1259 1260 /** 1261 * Registers a {@link AutofillCallback} to receive autofill events. 1262 * 1263 * @param callback callback to receive events. 1264 */ 1265 public void registerCallback(@Nullable AutofillCallback callback) { 1266 if (!hasAutofillFeature()) { 1267 return; 1268 } 1269 synchronized (mLock) { 1270 if (callback == null) return; 1271 1272 final boolean hadCallback = mCallback != null; 1273 mCallback = callback; 1274 1275 if (!hadCallback) { 1276 try { 1277 mService.setHasCallback(mSessionId, mContext.getUserId(), true); 1278 } catch (RemoteException e) { 1279 throw e.rethrowFromSystemServer(); 1280 } 1281 } 1282 } 1283 } 1284 1285 /** 1286 * Unregisters a {@link AutofillCallback} to receive autofill events. 1287 * 1288 * @param callback callback to stop receiving events. 1289 */ 1290 public void unregisterCallback(@Nullable AutofillCallback callback) { 1291 if (!hasAutofillFeature()) { 1292 return; 1293 } 1294 synchronized (mLock) { 1295 if (callback == null || mCallback == null || callback != mCallback) return; 1296 1297 mCallback = null; 1298 1299 try { 1300 mService.setHasCallback(mSessionId, mContext.getUserId(), false); 1301 } catch (RemoteException e) { 1302 throw e.rethrowFromSystemServer(); 1303 } 1304 } 1305 } 1306 1307 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 1308 Rect anchorBounds, IAutofillWindowPresenter presenter) { 1309 final View anchor = findView(id); 1310 if (anchor == null) { 1311 return; 1312 } 1313 1314 AutofillCallback callback = null; 1315 synchronized (mLock) { 1316 if (mSessionId == sessionId) { 1317 AutofillClient client = getClient(); 1318 1319 if (client != null) { 1320 if (client.autofillCallbackRequestShowFillUi(anchor, width, height, 1321 anchorBounds, presenter) && mCallback != null) { 1322 callback = mCallback; 1323 } 1324 } 1325 } 1326 } 1327 1328 if (callback != null) { 1329 if (id.isVirtual()) { 1330 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1331 AutofillCallback.EVENT_INPUT_SHOWN); 1332 } else { 1333 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); 1334 } 1335 } 1336 } 1337 1338 private void authenticate(int sessionId, int authenticationId, IntentSender intent, 1339 Intent fillInIntent) { 1340 synchronized (mLock) { 1341 if (sessionId == mSessionId) { 1342 final AutofillClient client = getClient(); 1343 if (client != null) { 1344 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent); 1345 } 1346 } 1347 } 1348 } 1349 1350 /** @hide */ 1351 public static final int SET_STATE_FLAG_ENABLED = 0x01; 1352 /** @hide */ 1353 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02; 1354 /** @hide */ 1355 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04; 1356 /** @hide */ 1357 public static final int SET_STATE_FLAG_DEBUG = 0x08; 1358 /** @hide */ 1359 public static final int SET_STATE_FLAG_VERBOSE = 0x10; 1360 1361 private void setState(int flags) { 1362 if (sVerbose) Log.v(TAG, "setState(" + flags + ")"); 1363 synchronized (mLock) { 1364 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0; 1365 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) { 1366 // Reset the session state 1367 resetSessionLocked(); 1368 } 1369 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) { 1370 // Reset connection to system 1371 mServiceClient = null; 1372 if (mServiceClientCleaner != null) { 1373 mServiceClientCleaner.clean(); 1374 mServiceClientCleaner = null; 1375 } 1376 } 1377 } 1378 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0; 1379 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0; 1380 } 1381 1382 /** 1383 * Sets a view as autofilled if the current value is the {code targetValue}. 1384 * 1385 * @param view The view that is to be autofilled 1386 * @param targetValue The value we want to fill into view 1387 */ 1388 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { 1389 AutofillValue currentValue = view.getAutofillValue(); 1390 if (Objects.equals(currentValue, targetValue)) { 1391 synchronized (mLock) { 1392 if (mLastAutofilledData == null) { 1393 mLastAutofilledData = new ParcelableMap(1); 1394 } 1395 mLastAutofilledData.put(getAutofillId(view), targetValue); 1396 } 1397 view.setAutofilled(true); 1398 } 1399 } 1400 1401 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 1402 synchronized (mLock) { 1403 if (sessionId != mSessionId) { 1404 return; 1405 } 1406 1407 final AutofillClient client = getClient(); 1408 if (client == null) { 1409 return; 1410 } 1411 1412 final int itemCount = ids.size(); 1413 int numApplied = 0; 1414 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; 1415 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids)); 1416 1417 for (int i = 0; i < itemCount; i++) { 1418 final AutofillId id = ids.get(i); 1419 final AutofillValue value = values.get(i); 1420 final int viewId = id.getViewId(); 1421 final View view = views[i]; 1422 if (view == null) { 1423 Log.w(TAG, "autofill(): no View with id " + viewId); 1424 continue; 1425 } 1426 if (id.isVirtual()) { 1427 if (virtualValues == null) { 1428 // Most likely there will be just one view with virtual children. 1429 virtualValues = new ArrayMap<>(1); 1430 } 1431 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); 1432 if (valuesByParent == null) { 1433 // We don't know the size yet, but usually it will be just a few fields... 1434 valuesByParent = new SparseArray<>(5); 1435 virtualValues.put(view, valuesByParent); 1436 } 1437 valuesByParent.put(id.getVirtualChildId(), value); 1438 } else { 1439 // Mark the view as to be autofilled with 'value' 1440 if (mLastAutofilledData == null) { 1441 mLastAutofilledData = new ParcelableMap(itemCount - i); 1442 } 1443 mLastAutofilledData.put(id, value); 1444 1445 view.autofill(value); 1446 1447 // Set as autofilled if the values match now, e.g. when the value was updated 1448 // synchronously. 1449 // If autofill happens async, the view is set to autofilled in 1450 // notifyValueChanged. 1451 setAutofilledIfValuesIs(view, value); 1452 1453 numApplied++; 1454 } 1455 } 1456 1457 if (virtualValues != null) { 1458 for (int i = 0; i < virtualValues.size(); i++) { 1459 final View parent = virtualValues.keyAt(i); 1460 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); 1461 parent.autofill(childrenValues); 1462 numApplied += childrenValues.size(); 1463 } 1464 } 1465 1466 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED) 1467 .setPackageName(mContext.getPackageName()) 1468 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount) 1469 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); 1470 mMetricsLogger.write(log); 1471 } 1472 } 1473 1474 /** 1475 * Set the tracked views. 1476 * 1477 * @param trackedIds The views to be tracked. 1478 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible. 1479 * @param saveOnFinish Finish the session once the activity is finished. 1480 * @param fillableIds Views that might anchor FillUI. 1481 * @param saveTriggerId View that when clicked triggers commit(). 1482 */ 1483 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, 1484 boolean saveOnAllViewsInvisible, boolean saveOnFinish, 1485 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { 1486 synchronized (mLock) { 1487 if (mEnabled && mSessionId == sessionId) { 1488 if (saveOnAllViewsInvisible) { 1489 mTrackedViews = new TrackedViews(trackedIds); 1490 } else { 1491 mTrackedViews = null; 1492 } 1493 mSaveOnFinish = saveOnFinish; 1494 if (fillableIds != null) { 1495 if (mFillableIds == null) { 1496 mFillableIds = new ArraySet<>(fillableIds.length); 1497 } 1498 for (AutofillId id : fillableIds) { 1499 mFillableIds.add(id); 1500 } 1501 if (sVerbose) { 1502 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds 1503 + ", mFillableIds" + mFillableIds); 1504 } 1505 } 1506 1507 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) { 1508 // Turn off trigger on previous view id. 1509 setNotifyOnClickLocked(mSaveTriggerId, false); 1510 } 1511 1512 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) { 1513 // Turn on trigger on new view id. 1514 mSaveTriggerId = saveTriggerId; 1515 setNotifyOnClickLocked(mSaveTriggerId, true); 1516 } 1517 } 1518 } 1519 } 1520 1521 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) { 1522 final View view = findView(id); 1523 if (view == null) { 1524 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id); 1525 return; 1526 } 1527 view.setNotifyAutofillManagerOnClick(notify); 1528 } 1529 1530 private void setSaveUiState(int sessionId, boolean shown) { 1531 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown); 1532 synchronized (mLock) { 1533 if (mSessionId != NO_SESSION) { 1534 // Race condition: app triggered a new session after the previous session was 1535 // finished but before server called setSaveUiState() - need to cancel the new 1536 // session to avoid further inconsistent behavior. 1537 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown 1538 + ") called on existing session " + mSessionId + "; cancelling it"); 1539 cancelSessionLocked(); 1540 } 1541 if (shown) { 1542 mSessionId = sessionId; 1543 mState = STATE_SHOWING_SAVE_UI; 1544 } else { 1545 mSessionId = NO_SESSION; 1546 mState = STATE_UNKNOWN; 1547 } 1548 } 1549 } 1550 1551 /** 1552 * Marks the state of the session as finished. 1553 * 1554 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null} 1555 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or 1556 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill 1557 * requests for the activity). 1558 */ 1559 private void setSessionFinished(int newState) { 1560 synchronized (mLock) { 1561 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState); 1562 resetSessionLocked(); 1563 mState = newState; 1564 } 1565 } 1566 1567 private void requestHideFillUi(AutofillId id) { 1568 final View anchor = findView(id); 1569 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor); 1570 if (anchor == null) { 1571 return; 1572 } 1573 requestHideFillUi(id, anchor); 1574 } 1575 1576 private void requestHideFillUi(AutofillId id, View anchor) { 1577 1578 AutofillCallback callback = null; 1579 synchronized (mLock) { 1580 // We do not check the session id for two reasons: 1581 // 1. If local and remote session id are off sync the UI would be stuck shown 1582 // 2. There is a race between the user state being destroyed due the fill 1583 // service being uninstalled and the UI being dismissed. 1584 AutofillClient client = getClient(); 1585 if (client != null) { 1586 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) { 1587 callback = mCallback; 1588 } 1589 } 1590 } 1591 1592 if (callback != null) { 1593 if (id.isVirtual()) { 1594 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1595 AutofillCallback.EVENT_INPUT_HIDDEN); 1596 } else { 1597 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); 1598 } 1599 } 1600 } 1601 1602 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) { 1603 if (sVerbose) { 1604 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id 1605 + ", sessionFinishedState=" + sessionFinishedState); 1606 } 1607 final View anchor = findView(id); 1608 if (anchor == null) { 1609 return; 1610 } 1611 1612 AutofillCallback callback = null; 1613 synchronized (mLock) { 1614 if (mSessionId == sessionId && getClient() != null) { 1615 callback = mCallback; 1616 } 1617 } 1618 1619 if (callback != null) { 1620 if (id.isVirtual()) { 1621 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1622 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1623 } else { 1624 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1625 } 1626 } 1627 1628 if (sessionFinishedState != 0) { 1629 // Callback call was "hijacked" to also update the session state. 1630 setSessionFinished(sessionFinishedState); 1631 } 1632 } 1633 1634 /** 1635 * Get an array of viewIds from a List of {@link AutofillId}. 1636 * 1637 * @param autofillIds The autofill ids to convert 1638 * 1639 * @return The array of viewIds. 1640 */ 1641 // TODO: move to Helper as static method 1642 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) { 1643 final int numIds = autofillIds.length; 1644 final int[] viewIds = new int[numIds]; 1645 for (int i = 0; i < numIds; i++) { 1646 viewIds[i] = autofillIds[i].getViewId(); 1647 } 1648 1649 return viewIds; 1650 } 1651 1652 // TODO: move to Helper as static method 1653 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) { 1654 final int numIds = autofillIds.size(); 1655 final int[] viewIds = new int[numIds]; 1656 for (int i = 0; i < numIds; i++) { 1657 viewIds[i] = autofillIds.get(i).getViewId(); 1658 } 1659 1660 return viewIds; 1661 } 1662 1663 /** 1664 * Find a single view by its id. 1665 * 1666 * @param autofillId The autofill id of the view 1667 * 1668 * @return The view or {@code null} if view was not found 1669 */ 1670 private View findView(@NonNull AutofillId autofillId) { 1671 final AutofillClient client = getClient(); 1672 1673 if (client == null) { 1674 return null; 1675 } 1676 1677 return client.findViewByAutofillIdTraversal(autofillId.getViewId()); 1678 } 1679 1680 /** @hide */ 1681 public boolean hasAutofillFeature() { 1682 return mService != null; 1683 } 1684 1685 /** @hide */ 1686 public void onPendingSaveUi(int operation, IBinder token) { 1687 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 1688 1689 synchronized (mLock) { 1690 try { 1691 mService.onPendingSaveUi(operation, token); 1692 } catch (RemoteException e) { 1693 e.rethrowFromSystemServer(); 1694 } 1695 } 1696 } 1697 1698 /** @hide */ 1699 public void dump(String outerPrefix, PrintWriter pw) { 1700 pw.print(outerPrefix); pw.println("AutofillManager:"); 1701 final String pfx = outerPrefix + " "; 1702 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId); 1703 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked()); 1704 pw.print(pfx); pw.print("context: "); pw.println(mContext); 1705 pw.print(pfx); pw.print("client: "); pw.println(getClient()); 1706 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled); 1707 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null); 1708 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null); 1709 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData); 1710 pw.print(pfx); pw.print("tracked views: "); 1711 if (mTrackedViews == null) { 1712 pw.println("null"); 1713 } else { 1714 final String pfx2 = pfx + " "; 1715 pw.println(); 1716 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds); 1717 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds); 1718 } 1719 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds); 1720 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); 1721 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); 1722 pw.print(pfx); pw.print("debug: "); pw.print(sDebug); 1723 pw.print(" verbose: "); pw.println(sVerbose); 1724 } 1725 1726 private String getStateAsStringLocked() { 1727 switch (mState) { 1728 case STATE_UNKNOWN: 1729 return "STATE_UNKNOWN"; 1730 case STATE_ACTIVE: 1731 return "STATE_ACTIVE"; 1732 case STATE_FINISHED: 1733 return "STATE_FINISHED"; 1734 case STATE_SHOWING_SAVE_UI: 1735 return "STATE_SHOWING_SAVE_UI"; 1736 case STATE_DISABLED_BY_SERVICE: 1737 return "STATE_DISABLED_BY_SERVICE"; 1738 default: 1739 return "INVALID:" + mState; 1740 } 1741 } 1742 1743 private boolean isActiveLocked() { 1744 return mState == STATE_ACTIVE; 1745 } 1746 1747 private boolean isDisabledByServiceLocked() { 1748 return mState == STATE_DISABLED_BY_SERVICE; 1749 } 1750 1751 private boolean isFinishedLocked() { 1752 return mState == STATE_FINISHED; 1753 } 1754 1755 private void post(Runnable runnable) { 1756 final AutofillClient client = getClient(); 1757 if (client == null) { 1758 if (sVerbose) Log.v(TAG, "ignoring post() because client is null"); 1759 return; 1760 } 1761 client.runOnUiThread(runnable); 1762 } 1763 1764 /** 1765 * View tracking information. Once all tracked views become invisible the session is finished. 1766 */ 1767 private class TrackedViews { 1768 /** Visible tracked views */ 1769 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; 1770 1771 /** Invisible tracked views */ 1772 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; 1773 1774 /** 1775 * Check if set is null or value is in set. 1776 * 1777 * @param set The set or null (== empty set) 1778 * @param value The value that might be in the set 1779 * 1780 * @return {@code true} iff set is not empty and value is in set 1781 */ 1782 // TODO: move to Helper as static method 1783 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) { 1784 return set != null && set.contains(value); 1785 } 1786 1787 /** 1788 * Add a value to a set. If set is null, create a new set. 1789 * 1790 * @param set The set or null (== empty set) 1791 * @param valueToAdd The value to add 1792 * 1793 * @return The set including the new value. If set was {@code null}, a set containing only 1794 * the new value. 1795 */ 1796 // TODO: move to Helper as static method 1797 @NonNull 1798 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) { 1799 if (set == null) { 1800 set = new ArraySet<>(1); 1801 } 1802 1803 set.add(valueToAdd); 1804 1805 return set; 1806 } 1807 1808 /** 1809 * Remove a value from a set. 1810 * 1811 * @param set The set or null (== empty set) 1812 * @param valueToRemove The value to remove 1813 * 1814 * @return The set without the removed value. {@code null} if set was null, or is empty 1815 * after removal. 1816 */ 1817 // TODO: move to Helper as static method 1818 @Nullable 1819 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) { 1820 if (set == null) { 1821 return null; 1822 } 1823 1824 set.remove(valueToRemove); 1825 1826 if (set.isEmpty()) { 1827 return null; 1828 } 1829 1830 return set; 1831 } 1832 1833 /** 1834 * Set the tracked views. 1835 * 1836 * @param trackedIds The views to be tracked 1837 */ 1838 TrackedViews(@Nullable AutofillId[] trackedIds) { 1839 final AutofillClient client = getClient(); 1840 if (trackedIds != null && client != null) { 1841 final boolean[] isVisible; 1842 1843 if (client.isVisibleForAutofill()) { 1844 isVisible = client.getViewVisibility(getViewIds(trackedIds)); 1845 } else { 1846 // All false 1847 isVisible = new boolean[trackedIds.length]; 1848 } 1849 1850 final int numIds = trackedIds.length; 1851 for (int i = 0; i < numIds; i++) { 1852 final AutofillId id = trackedIds[i]; 1853 1854 if (isVisible[i]) { 1855 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1856 } else { 1857 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1858 } 1859 } 1860 } 1861 1862 if (sVerbose) { 1863 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " 1864 + " mVisibleTrackedIds=" + mVisibleTrackedIds 1865 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); 1866 } 1867 1868 if (mVisibleTrackedIds == null) { 1869 finishSessionLocked(); 1870 } 1871 } 1872 1873 /** 1874 * Called when a {@link View view's} visibility changes. 1875 * 1876 * @param id the id of the view/virtual view whose visibility changed. 1877 * @param isVisible visible if the view is visible in the view hierarchy. 1878 */ 1879 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) { 1880 AutofillClient client = getClient(); 1881 1882 if (sDebug) { 1883 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible=" 1884 + isVisible); 1885 } 1886 1887 if (client != null && client.isVisibleForAutofill()) { 1888 if (isVisible) { 1889 if (isInSet(mInvisibleTrackedIds, id)) { 1890 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); 1891 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1892 } 1893 } else { 1894 if (isInSet(mVisibleTrackedIds, id)) { 1895 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); 1896 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1897 } 1898 } 1899 } 1900 1901 if (mVisibleTrackedIds == null) { 1902 if (sVerbose) { 1903 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds); 1904 } 1905 finishSessionLocked(); 1906 } 1907 } 1908 1909 /** 1910 * Called once the client becomes visible. 1911 * 1912 * @see AutofillClient#isVisibleForAutofill() 1913 */ 1914 void onVisibleForAutofillLocked() { 1915 // The visibility of the views might have changed while the client was not be visible, 1916 // hence update the visibility state for all views. 1917 AutofillClient client = getClient(); 1918 ArraySet<AutofillId> updatedVisibleTrackedIds = null; 1919 ArraySet<AutofillId> updatedInvisibleTrackedIds = null; 1920 if (client != null) { 1921 if (mInvisibleTrackedIds != null) { 1922 final ArrayList<AutofillId> orderedInvisibleIds = 1923 new ArrayList<>(mInvisibleTrackedIds); 1924 final boolean[] isVisible = client.getViewVisibility( 1925 getViewIds(orderedInvisibleIds)); 1926 1927 final int numInvisibleTrackedIds = orderedInvisibleIds.size(); 1928 for (int i = 0; i < numInvisibleTrackedIds; i++) { 1929 final AutofillId id = orderedInvisibleIds.get(i); 1930 if (isVisible[i]) { 1931 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1932 1933 if (sDebug) { 1934 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible"); 1935 } 1936 } else { 1937 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1938 } 1939 } 1940 } 1941 1942 if (mVisibleTrackedIds != null) { 1943 final ArrayList<AutofillId> orderedVisibleIds = 1944 new ArrayList<>(mVisibleTrackedIds); 1945 final boolean[] isVisible = client.getViewVisibility( 1946 getViewIds(orderedVisibleIds)); 1947 1948 final int numVisibleTrackedIds = orderedVisibleIds.size(); 1949 for (int i = 0; i < numVisibleTrackedIds; i++) { 1950 final AutofillId id = orderedVisibleIds.get(i); 1951 1952 if (isVisible[i]) { 1953 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1954 } else { 1955 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1956 1957 if (sDebug) { 1958 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible"); 1959 } 1960 } 1961 } 1962 } 1963 1964 mInvisibleTrackedIds = updatedInvisibleTrackedIds; 1965 mVisibleTrackedIds = updatedVisibleTrackedIds; 1966 } 1967 1968 if (mVisibleTrackedIds == null) { 1969 finishSessionLocked(); 1970 } 1971 } 1972 } 1973 1974 /** 1975 * Callback for autofill related events. 1976 * 1977 * <p>Typically used for applications that display their own "auto-complete" views, so they can 1978 * enable / disable such views when the autofill UI is shown / hidden. 1979 */ 1980 public abstract static class AutofillCallback { 1981 1982 /** @hide */ 1983 @IntDef(prefix = { "EVENT_INPUT_" }, value = { 1984 EVENT_INPUT_SHOWN, 1985 EVENT_INPUT_HIDDEN, 1986 EVENT_INPUT_UNAVAILABLE 1987 }) 1988 @Retention(RetentionPolicy.SOURCE) 1989 public @interface AutofillEventType {} 1990 1991 /** 1992 * The autofill input UI associated with the view was shown. 1993 * 1994 * <p>If the view provides its own auto-complete UI and its currently shown, it 1995 * should be hidden upon receiving this event. 1996 */ 1997 public static final int EVENT_INPUT_SHOWN = 1; 1998 1999 /** 2000 * The autofill input UI associated with the view was hidden. 2001 * 2002 * <p>If the view provides its own auto-complete UI that was hidden upon a 2003 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now. 2004 */ 2005 public static final int EVENT_INPUT_HIDDEN = 2; 2006 2007 /** 2008 * The autofill input UI associated with the view isn't shown because 2009 * autofill is not available. 2010 * 2011 * <p>If the view provides its own auto-complete UI but was not displaying it 2012 * to avoid flickering, it could shown it upon receiving this event. 2013 */ 2014 public static final int EVENT_INPUT_UNAVAILABLE = 3; 2015 2016 /** 2017 * Called after a change in the autofill state associated with a view. 2018 * 2019 * @param view view associated with the change. 2020 * 2021 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 2022 */ 2023 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) { 2024 } 2025 2026 /** 2027 * Called after a change in the autofill state associated with a virtual view. 2028 * 2029 * @param view parent view associated with the change. 2030 * @param virtualId id identifying the virtual child inside the parent view. 2031 * 2032 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 2033 */ 2034 public void onAutofillEvent(@NonNull View view, int virtualId, 2035 @AutofillEventType int event) { 2036 } 2037 } 2038 2039 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub { 2040 private final WeakReference<AutofillManager> mAfm; 2041 2042 AutofillManagerClient(AutofillManager autofillManager) { 2043 mAfm = new WeakReference<>(autofillManager); 2044 } 2045 2046 @Override 2047 public void setState(int flags) { 2048 final AutofillManager afm = mAfm.get(); 2049 if (afm != null) { 2050 afm.post(() -> afm.setState(flags)); 2051 } 2052 } 2053 2054 @Override 2055 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 2056 final AutofillManager afm = mAfm.get(); 2057 if (afm != null) { 2058 afm.post(() -> afm.autofill(sessionId, ids, values)); 2059 } 2060 } 2061 2062 @Override 2063 public void authenticate(int sessionId, int authenticationId, IntentSender intent, 2064 Intent fillInIntent) { 2065 final AutofillManager afm = mAfm.get(); 2066 if (afm != null) { 2067 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent)); 2068 } 2069 } 2070 2071 @Override 2072 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 2073 Rect anchorBounds, IAutofillWindowPresenter presenter) { 2074 final AutofillManager afm = mAfm.get(); 2075 if (afm != null) { 2076 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds, 2077 presenter)); 2078 } 2079 } 2080 2081 @Override 2082 public void requestHideFillUi(int sessionId, AutofillId id) { 2083 final AutofillManager afm = mAfm.get(); 2084 if (afm != null) { 2085 afm.post(() -> afm.requestHideFillUi(id)); 2086 } 2087 } 2088 2089 @Override 2090 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) { 2091 final AutofillManager afm = mAfm.get(); 2092 if (afm != null) { 2093 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState)); 2094 } 2095 } 2096 2097 @Override 2098 public void startIntentSender(IntentSender intentSender, Intent intent) { 2099 final AutofillManager afm = mAfm.get(); 2100 if (afm != null) { 2101 afm.post(() -> { 2102 try { 2103 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0); 2104 } catch (IntentSender.SendIntentException e) { 2105 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e); 2106 } 2107 }); 2108 } 2109 } 2110 2111 @Override 2112 public void setTrackedViews(int sessionId, AutofillId[] ids, 2113 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds, 2114 AutofillId saveTriggerId) { 2115 final AutofillManager afm = mAfm.get(); 2116 if (afm != null) { 2117 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, 2118 saveOnFinish, fillableIds, saveTriggerId)); 2119 } 2120 } 2121 2122 @Override 2123 public void setSaveUiState(int sessionId, boolean shown) { 2124 final AutofillManager afm = mAfm.get(); 2125 if (afm != null) { 2126 afm.post(() -> afm.setSaveUiState(sessionId, shown)); 2127 } 2128 } 2129 2130 @Override 2131 public void setSessionFinished(int newState) { 2132 final AutofillManager afm = mAfm.get(); 2133 if (afm != null) { 2134 afm.post(() -> afm.setSessionFinished(newState)); 2135 } 2136 } 2137 } 2138} 2139