AutofillManager.java revision 0c8ce322a3107933d16082e00c77fbe501dc3c07
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.accessibilityservice.AccessibilityServiceInfo; 24import android.annotation.IntDef; 25import android.annotation.NonNull; 26import android.annotation.Nullable; 27import android.annotation.RequiresFeature; 28import android.annotation.SystemService; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentSender; 33import android.content.pm.PackageManager; 34import android.content.pm.ResolveInfo; 35import android.graphics.Rect; 36import android.metrics.LogMaker; 37import android.os.Bundle; 38import android.os.IBinder; 39import android.os.Parcelable; 40import android.os.RemoteException; 41import android.service.autofill.AutofillService; 42import android.service.autofill.FillEventHistory; 43import android.service.autofill.UserData; 44import android.util.ArrayMap; 45import android.util.ArraySet; 46import android.util.Log; 47import android.util.SparseArray; 48import android.view.Choreographer; 49import android.view.KeyEvent; 50import android.view.View; 51import android.view.accessibility.AccessibilityEvent; 52import android.view.accessibility.AccessibilityManager; 53import android.view.accessibility.AccessibilityNodeInfo; 54import android.view.accessibility.AccessibilityNodeProvider; 55import android.view.accessibility.AccessibilityWindowInfo; 56 57import com.android.internal.annotations.GuardedBy; 58import com.android.internal.logging.MetricsLogger; 59import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 60import com.android.internal.util.ArrayUtils; 61import com.android.internal.util.Preconditions; 62 63import org.xmlpull.v1.XmlPullParserException; 64 65import java.io.IOException; 66import java.io.PrintWriter; 67import java.lang.annotation.Retention; 68import java.lang.annotation.RetentionPolicy; 69import java.lang.ref.WeakReference; 70import java.util.ArrayList; 71import java.util.Arrays; 72import java.util.Collections; 73import java.util.List; 74import java.util.Objects; 75 76//TODO: use java.lang.ref.Cleaner once Android supports Java 9 77import sun.misc.Cleaner; 78 79/** 80 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the 81 * Autofill Framework lifecycle. 82 * 83 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an 84 * activity context; the autofill context is created when one of the following methods is called for 85 * the first time in an activity context, and the current user has an enabled autofill service: 86 * 87 * <ul> 88 * <li>{@link #notifyViewEntered(View)} 89 * <li>{@link #notifyViewEntered(View, int, Rect)} 90 * <li>{@link #requestAutofill(View)} 91 * </ul> 92 * 93 * <p>Tipically, the context is automatically created when the first view of the activity is 94 * focused because {@code View.onFocusChanged()} indirectly calls 95 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to 96 * explicitly create it (for example, a custom view developer could offer a contextual menu action 97 * in a text-field view to let users manually request autofill). 98 * 99 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure} 100 * that represents the view hierarchy by calling 101 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views 102 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in 103 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and 104 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in 105 * the hierarchy. 106 * 107 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which 108 * parses it looking for views that can be autofilled. If the service finds such views, it returns 109 * a data structure to the Android System containing the following optional info: 110 * 111 * <ul> 112 * <li>Datasets used to autofill subsets of views in the activity. 113 * <li>Id of views that the service can save their values for future autofilling. 114 * </ul> 115 * 116 * <p>When the service returns datasets, the Android System displays an autofill dataset picker 117 * UI associated with the view, when the view is focused on and is part of a dataset. 118 * The application can be notified when the UI is shown by registering an 119 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user 120 * selects a dataset from the UI, all views present in the dataset are autofilled, through 121 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}. 122 * 123 * <p>When the service returns ids of savable views, the Android System keeps track of changes 124 * made to these views, so they can be used to determine if the autofill save UI is shown later. 125 * 126 * <p>The context is then finished when one of the following occurs: 127 * 128 * <ul> 129 * <li>{@link #commit()} is called or all savable views are gone. 130 * <li>{@link #cancel()} is called. 131 * </ul> 132 * 133 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System 134 * shows an autofill save UI if the value of savable views have changed. If the user selects the 135 * option to Save, the current value of the views is then sent to the autofill service. 136 * 137 * <p>It is safe to call into its methods from any thread. 138 */ 139@SystemService(Context.AUTOFILL_MANAGER_SERVICE) 140@RequiresFeature(PackageManager.FEATURE_AUTOFILL) 141public final class AutofillManager { 142 143 private static final String TAG = "AutofillManager"; 144 145 /** 146 * Intent extra: The assist structure which captures the filled screen. 147 * 148 * <p> 149 * Type: {@link android.app.assist.AssistStructure} 150 */ 151 public static final String EXTRA_ASSIST_STRUCTURE = 152 "android.view.autofill.extra.ASSIST_STRUCTURE"; 153 154 /** 155 * Intent extra: The result of an authentication operation. It is 156 * either a fully populated {@link android.service.autofill.FillResponse} 157 * or a fully populated {@link android.service.autofill.Dataset} if 158 * a response or a dataset is being authenticated respectively. 159 * 160 * <p> 161 * Type: {@link android.service.autofill.FillResponse} or a 162 * {@link android.service.autofill.Dataset} 163 */ 164 public static final String EXTRA_AUTHENTICATION_RESULT = 165 "android.view.autofill.extra.AUTHENTICATION_RESULT"; 166 167 /** 168 * Intent extra: The optional extras provided by the 169 * {@link android.service.autofill.AutofillService}. 170 * 171 * <p>For example, when the service responds to a {@link 172 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with 173 * a {@code FillResponse} that requires authentication, the Intent that launches the 174 * service authentication will contain the Bundle set by 175 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra. 176 * 177 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service 178 * can also add this bundle to the {@link Intent} set as the 179 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request, 180 * so the bundle can be recovered later on 181 * {@link android.service.autofill.SaveRequest#getClientState()}. 182 * 183 * <p> 184 * Type: {@link android.os.Bundle} 185 */ 186 public static final String EXTRA_CLIENT_STATE = 187 "android.view.autofill.extra.CLIENT_STATE"; 188 189 /** @hide */ 190 public static final String EXTRA_RESTORE_SESSION_TOKEN = 191 "android.view.autofill.extra.RESTORE_SESSION_TOKEN"; 192 193 private static final String SESSION_ID_TAG = "android:sessionId"; 194 private static final String STATE_TAG = "android:state"; 195 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; 196 197 /** @hide */ public static final int ACTION_START_SESSION = 1; 198 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2; 199 /** @hide */ public static final int ACTION_VIEW_EXITED = 3; 200 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4; 201 202 203 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1; 204 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; 205 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; 206 207 /** Which bits in an authentication id are used for the dataset id */ 208 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF; 209 /** How many bits in an authentication id are used for the dataset id */ 210 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16; 211 /** @hide The index for an undefined data set */ 212 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF; 213 214 /** 215 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI. 216 * 217 * @hide 218 */ 219 public static final int PENDING_UI_OPERATION_CANCEL = 1; 220 221 /** 222 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI. 223 * 224 * @hide 225 */ 226 public static final int PENDING_UI_OPERATION_RESTORE = 2; 227 228 /** 229 * Initial state of the autofill context, set when there is no session (i.e., when 230 * {@link #mSessionId} is {@link #NO_SESSION}). 231 * 232 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to 233 * the server. 234 * 235 * @hide 236 */ 237 public static final int STATE_UNKNOWN = 0; 238 239 /** 240 * State where the autofill context hasn't been {@link #commit() finished} nor 241 * {@link #cancel() canceled} yet. 242 * 243 * @hide 244 */ 245 public static final int STATE_ACTIVE = 1; 246 247 /** 248 * State where the autofill context was finished by the server because the autofill 249 * service could not autofill the activity. 250 * 251 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored, 252 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}). 253 * 254 * @hide 255 */ 256 public static final int STATE_FINISHED = 2; 257 258 /** 259 * State where the autofill context has been {@link #commit() finished} but the server still has 260 * a session because the Save UI hasn't been dismissed yet. 261 * 262 * @hide 263 */ 264 public static final int STATE_SHOWING_SAVE_UI = 3; 265 266 /** 267 * State where the autofill is disabled because the service cannot autofill the activity at all. 268 * 269 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)} 270 * (and {@link #requestAutofill(View, int, Rect)}). 271 * 272 * @hide 273 */ 274 public static final int STATE_DISABLED_BY_SERVICE = 4; 275 276 /** 277 * Same as {@link #STATE_UNKNOWN}, but used on 278 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because 279 * the URL bar changed on client mode 280 * 281 * @hide 282 */ 283 public static final int STATE_UNKNOWN_COMPAT_MODE = 5; 284 285 286 /** 287 * Timeout in ms for calls to the field classification service. 288 * @hide 289 */ 290 public static final int FC_SERVICE_TIMEOUT = 5000; 291 292 /** 293 * Makes an authentication id from a request id and a dataset id. 294 * 295 * @param requestId The request id. 296 * @param datasetId The dataset id. 297 * @return The authentication id. 298 * @hide 299 */ 300 public static int makeAuthenticationId(int requestId, int datasetId) { 301 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT) 302 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK); 303 } 304 305 /** 306 * Gets the request id from an authentication id. 307 * 308 * @param authRequestId The authentication id. 309 * @return The request id. 310 * @hide 311 */ 312 public static int getRequestIdFromAuthenticationId(int authRequestId) { 313 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT); 314 } 315 316 /** 317 * Gets the dataset id from an authentication id. 318 * 319 * @param authRequestId The authentication id. 320 * @return The dataset id. 321 * @hide 322 */ 323 public static int getDatasetIdFromAuthenticationId(int authRequestId) { 324 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK); 325 } 326 327 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 328 329 /** 330 * There is currently no session running. 331 * {@hide} 332 */ 333 public static final int NO_SESSION = Integer.MIN_VALUE; 334 335 private final IAutoFillManager mService; 336 337 private final Object mLock = new Object(); 338 339 @GuardedBy("mLock") 340 private IAutoFillManagerClient mServiceClient; 341 342 @GuardedBy("mLock") 343 private Cleaner mServiceClientCleaner; 344 345 @GuardedBy("mLock") 346 private AutofillCallback mCallback; 347 348 private final Context mContext; 349 350 @GuardedBy("mLock") 351 private int mSessionId = NO_SESSION; 352 353 @GuardedBy("mLock") 354 private int mState = STATE_UNKNOWN; 355 356 @GuardedBy("mLock") 357 private boolean mEnabled; 358 359 /** If a view changes to this mapping the autofill operation was successful */ 360 @GuardedBy("mLock") 361 @Nullable private ParcelableMap mLastAutofilledData; 362 363 /** If view tracking is enabled, contains the tracking state */ 364 @GuardedBy("mLock") 365 @Nullable private TrackedViews mTrackedViews; 366 367 /** Views that are only tracked because they are fillable and could be anchoring the UI. */ 368 @GuardedBy("mLock") 369 @Nullable private ArraySet<AutofillId> mFillableIds; 370 371 /** id of last requested autofill ui */ 372 @Nullable private AutofillId mIdShownFillUi; 373 374 /** 375 * Views that were already "entered" - if they're entered again when the session is not active, 376 * they're ignored 377 * */ 378 @GuardedBy("mLock") 379 @Nullable private ArraySet<AutofillId> mEnteredIds; 380 381 /** If set, session is commited when the field is clicked. */ 382 @GuardedBy("mLock") 383 @Nullable private AutofillId mSaveTriggerId; 384 385 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */ 386 @GuardedBy("mLock") 387 private boolean mOnInvisibleCalled; 388 389 /** If set, session is commited when the activity is finished; otherwise session is canceled. */ 390 @GuardedBy("mLock") 391 private boolean mSaveOnFinish; 392 393 /** If compatibility mode is enabled - this is a bridge to interact with a11y */ 394 @GuardedBy("mLock") 395 private CompatibilityBridge mCompatibilityBridge; 396 397 /** @hide */ 398 public interface AutofillClient { 399 /** 400 * Asks the client to start an authentication flow. 401 * 402 * @param authenticationId A unique id of the authentication operation. 403 * @param intent The authentication intent. 404 * @param fillInIntent The authentication fill-in intent. 405 */ 406 void autofillClientAuthenticate(int authenticationId, IntentSender intent, 407 Intent fillInIntent); 408 409 /** 410 * Tells the client this manager has state to be reset. 411 */ 412 void autofillClientResetableStateAvailable(); 413 414 /** 415 * Request showing the autofill UI. 416 * 417 * @param anchor The real view the UI needs to anchor to. 418 * @param width The width of the fill UI content. 419 * @param height The height of the fill UI content. 420 * @param virtualBounds The bounds of the virtual decendant of the anchor. 421 * @param presenter The presenter that controls the fill UI window. 422 * @return Whether the UI was shown. 423 */ 424 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height, 425 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); 426 427 /** 428 * Dispatch unhandled keyevent from Autofill window 429 * @param anchor The real view the UI needs to anchor to. 430 * @param keyEvent Unhandled KeyEvent from autofill window. 431 */ 432 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent); 433 434 /** 435 * Request hiding the autofill UI. 436 * 437 * @return Whether the UI was hidden. 438 */ 439 boolean autofillClientRequestHideFillUi(); 440 441 /** 442 * Gets whether the fill UI is currenlty being shown. 443 * 444 * @return Whether the fill UI is currently being shown 445 */ 446 boolean autofillClientIsFillUiShowing(); 447 448 /** 449 * Checks if views are currently attached and visible. 450 * 451 * @return And array with {@code true} iff the view is attached or visible 452 */ 453 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds); 454 455 /** 456 * Checks is the client is currently visible as understood by autofill. 457 * 458 * @return {@code true} if the client is currently visible 459 */ 460 boolean autofillClientIsVisibleForAutofill(); 461 462 /** 463 * Client might disable enter/exit event e.g. when activity is paused. 464 */ 465 boolean isDisablingEnterExitEventForAutofill(); 466 467 /** 468 * Finds views by traversing the hierarchies of the client. 469 * 470 * @param autofillIds The autofill ids of the views to find 471 * 472 * @return And array containing the views (empty if no views found). 473 */ 474 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal( 475 @NonNull AutofillId[] autofillIds); 476 477 /** 478 * Finds a view by traversing the hierarchies of the client. 479 * 480 * @param autofillId The autofill id of the views to find 481 * 482 * @return The view, or {@code null} if not found 483 */ 484 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId); 485 486 /** 487 * Finds a view by a11y id in a given client window. 488 * 489 * @param viewId The accessibility id of the views to find 490 * @param windowId The accessibility window id where to search 491 * 492 * @return The view, or {@code null} if not found 493 */ 494 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId); 495 496 /** 497 * Runs the specified action on the UI thread. 498 */ 499 void autofillClientRunOnUiThread(Runnable action); 500 501 /** 502 * Gets the complete component name of this client. 503 */ 504 ComponentName autofillClientGetComponentName(); 505 506 /** 507 * Gets the activity token 508 */ 509 @Nullable IBinder autofillClientGetActivityToken(); 510 511 /** 512 * @return Whether compatibility mode is enabled. 513 */ 514 boolean autofillClientIsCompatibilityModeEnabled(); 515 516 /** 517 * Gets the next unique autofill ID. 518 * 519 * <p>Typically used to manage views whose content is recycled - see 520 * {@link View#setAutofillId(AutofillId)} for more info. 521 * 522 * @return An ID that is unique in the activity. 523 */ 524 @Nullable AutofillId autofillClientGetNextAutofillId(); 525 } 526 527 /** 528 * @hide 529 */ 530 public AutofillManager(Context context, IAutoFillManager service) { 531 mContext = Preconditions.checkNotNull(context, "context cannot be null"); 532 mService = service; 533 } 534 535 /** 536 * @hide 537 */ 538 public void enableCompatibilityMode() { 539 synchronized (mLock) { 540 // The accessibility manager is a singleton so we may need to plug 541 // different bridge based on which activity is currently focused 542 // in the current process. Since compat would be rarely used, just 543 // create and register a new instance every time. 544 mCompatibilityBridge = new CompatibilityBridge(); 545 } 546 } 547 548 /** 549 * Restore state after activity lifecycle 550 * 551 * @param savedInstanceState The state to be restored 552 * 553 * {@hide} 554 */ 555 public void onCreate(Bundle savedInstanceState) { 556 if (!hasAutofillFeature()) { 557 return; 558 } 559 synchronized (mLock) { 560 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG); 561 562 if (isActiveLocked()) { 563 Log.w(TAG, "New session was started before onCreate()"); 564 return; 565 } 566 567 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION); 568 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN); 569 570 if (mSessionId != NO_SESSION) { 571 ensureServiceClientAddedIfNeededLocked(); 572 573 final AutofillClient client = getClient(); 574 if (client != null) { 575 try { 576 final boolean sessionWasRestored = mService.restoreSession(mSessionId, 577 client.autofillClientGetActivityToken(), 578 mServiceClient.asBinder()); 579 580 if (!sessionWasRestored) { 581 Log.w(TAG, "Session " + mSessionId + " could not be restored"); 582 mSessionId = NO_SESSION; 583 mState = STATE_UNKNOWN; 584 } else { 585 if (sDebug) { 586 Log.d(TAG, "session " + mSessionId + " was restored"); 587 } 588 589 client.autofillClientResetableStateAvailable(); 590 } 591 } catch (RemoteException e) { 592 Log.e(TAG, "Could not figure out if there was an autofill session", e); 593 } 594 } 595 } 596 } 597 } 598 599 /** 600 * Called once the client becomes visible. 601 * 602 * @see AutofillClient#autofillClientIsVisibleForAutofill() 603 * 604 * {@hide} 605 */ 606 public void onVisibleForAutofill() { 607 // This gets called when the client just got visible at which point the visibility 608 // of the tracked views may not have been computed (due to a pending layout, etc). 609 // While generally we have no way to know when the UI has settled. We will evaluate 610 // the tracked views state at the end of next frame to guarantee that everything 611 // that may need to be laid out is laid out. 612 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> { 613 synchronized (mLock) { 614 if (mEnabled && isActiveLocked() && mTrackedViews != null) { 615 mTrackedViews.onVisibleForAutofillChangedLocked(); 616 } 617 } 618 }, null); 619 } 620 621 /** 622 * Called once the client becomes invisible. 623 * 624 * @see AutofillClient#autofillClientIsVisibleForAutofill() 625 * 626 * {@hide} 627 */ 628 public void onInvisibleForAutofill() { 629 synchronized (mLock) { 630 mOnInvisibleCalled = true; 631 } 632 } 633 634 /** 635 * Save state before activity lifecycle 636 * 637 * @param outState Place to store the state 638 * 639 * {@hide} 640 */ 641 public void onSaveInstanceState(Bundle outState) { 642 if (!hasAutofillFeature()) { 643 return; 644 } 645 synchronized (mLock) { 646 if (mSessionId != NO_SESSION) { 647 outState.putInt(SESSION_ID_TAG, mSessionId); 648 } 649 if (mState != STATE_UNKNOWN) { 650 outState.putInt(STATE_TAG, mState); 651 } 652 if (mLastAutofilledData != null) { 653 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData); 654 } 655 } 656 } 657 658 /** 659 * @hide 660 */ 661 @GuardedBy("mLock") 662 public boolean isCompatibilityModeEnabledLocked() { 663 return mCompatibilityBridge != null; 664 } 665 666 /** 667 * Checks whether autofill is enabled for the current user. 668 * 669 * <p>Typically used to determine whether the option to explicitly request autofill should 670 * be offered - see {@link #requestAutofill(View)}. 671 * 672 * @return whether autofill is enabled for the current user. 673 */ 674 public boolean isEnabled() { 675 if (!hasAutofillFeature()) { 676 return false; 677 } 678 synchronized (mLock) { 679 if (isDisabledByServiceLocked()) { 680 return false; 681 } 682 ensureServiceClientAddedIfNeededLocked(); 683 return mEnabled; 684 } 685 } 686 687 /** 688 * Should always be called from {@link AutofillService#getFillEventHistory()}. 689 * 690 * @hide 691 */ 692 @Nullable public FillEventHistory getFillEventHistory() { 693 try { 694 return mService.getFillEventHistory(); 695 } catch (RemoteException e) { 696 e.rethrowFromSystemServer(); 697 return null; 698 } 699 } 700 701 /** 702 * Explicitly requests a new autofill context. 703 * 704 * <p>Normally, the autofill context is automatically started if necessary when 705 * {@link #notifyViewEntered(View)} is called, but this method should be used in the 706 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL 707 * option on its contextual overflow menu, and the user selects it. 708 * 709 * @param view view requesting the new autofill context. 710 */ 711 public void requestAutofill(@NonNull View view) { 712 notifyViewEntered(view, FLAG_MANUAL_REQUEST); 713 } 714 715 /** 716 * Explicitly requests a new autofill context for virtual views. 717 * 718 * <p>Normally, the autofill context is automatically started if necessary when 719 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the 720 * cases where it must be explicitly started. For example, when the virtual view offers an 721 * AUTOFILL option on its contextual overflow menu, and the user selects it. 722 * 723 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 724 * parent view uses {@code bounds} to draw the virtual view inside its Canvas, 725 * the absolute bounds could be calculated by: 726 * 727 * <pre class="prettyprint"> 728 * int offset[] = new int[2]; 729 * getLocationOnScreen(offset); 730 * Rect absBounds = new Rect(bounds.left + offset[0], 731 * bounds.top + offset[1], 732 * bounds.right + offset[0], bounds.bottom + offset[1]); 733 * </pre> 734 * 735 * @param view the virtual view parent. 736 * @param virtualId id identifying the virtual child inside the parent view. 737 * @param absBounds absolute boundaries of the virtual view in the screen. 738 */ 739 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 740 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST); 741 } 742 743 /** 744 * Called when a {@link View} that supports autofill is entered. 745 * 746 * @param view {@link View} that was entered. 747 */ 748 public void notifyViewEntered(@NonNull View view) { 749 notifyViewEntered(view, 0); 750 } 751 752 @GuardedBy("mLock") 753 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) { 754 if (isDisabledByServiceLocked()) { 755 if (sVerbose) { 756 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id 757 + ") on state " + getStateAsStringLocked() + " because disabled by svc"); 758 } 759 return true; 760 } 761 if (isFinishedLocked()) { 762 // Session already finished: ignore if automatic request and view already entered 763 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null 764 && mEnteredIds.contains(id)) { 765 if (sVerbose) { 766 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id 767 + ") on state " + getStateAsStringLocked() 768 + " because view was already entered: " + mEnteredIds); 769 } 770 return true; 771 } 772 } 773 if (sVerbose) { 774 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id 775 + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds); 776 } 777 return false; 778 } 779 780 private boolean isClientVisibleForAutofillLocked() { 781 final AutofillClient client = getClient(); 782 return client != null && client.autofillClientIsVisibleForAutofill(); 783 } 784 785 private boolean isClientDisablingEnterExitEvent() { 786 final AutofillClient client = getClient(); 787 return client != null && client.isDisablingEnterExitEventForAutofill(); 788 } 789 790 private void notifyViewEntered(@NonNull View view, int flags) { 791 if (!hasAutofillFeature()) { 792 return; 793 } 794 AutofillCallback callback; 795 synchronized (mLock) { 796 callback = notifyViewEnteredLocked(view, flags); 797 } 798 799 if (callback != null) { 800 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 801 } 802 } 803 804 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ 805 @GuardedBy("mLock") 806 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) { 807 final AutofillId id = view.getAutofillId(); 808 if (shouldIgnoreViewEnteredLocked(id, flags)) return null; 809 810 AutofillCallback callback = null; 811 812 ensureServiceClientAddedIfNeededLocked(); 813 814 if (!mEnabled) { 815 if (mCallback != null) { 816 callback = mCallback; 817 } 818 } else { 819 // don't notify entered when Activity is already in background 820 if (!isClientDisablingEnterExitEvent()) { 821 final AutofillValue value = view.getAutofillValue(); 822 823 if (!isActiveLocked()) { 824 // Starts new session. 825 startSessionLocked(id, null, value, flags); 826 } else { 827 // Update focus on existing session. 828 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); 829 } 830 addEnteredIdLocked(id); 831 } 832 } 833 return callback; 834 } 835 836 /** 837 * Called when a {@link View} that supports autofill is exited. 838 * 839 * @param view {@link View} that was exited. 840 */ 841 public void notifyViewExited(@NonNull View view) { 842 if (!hasAutofillFeature()) { 843 return; 844 } 845 synchronized (mLock) { 846 notifyViewExitedLocked(view); 847 } 848 } 849 850 @GuardedBy("mLock") 851 void notifyViewExitedLocked(@NonNull View view) { 852 ensureServiceClientAddedIfNeededLocked(); 853 854 if (mEnabled && isActiveLocked()) { 855 // dont notify exited when Activity is already in background 856 if (!isClientDisablingEnterExitEvent()) { 857 final AutofillId id = view.getAutofillId(); 858 859 // Update focus on existing session. 860 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 861 } 862 } 863 } 864 865 /** 866 * Called when a {@link View view's} visibility changed. 867 * 868 * @param view {@link View} that was exited. 869 * @param isVisible visible if the view is visible in the view hierarchy. 870 */ 871 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) { 872 notifyViewVisibilityChangedInternal(view, 0, isVisible, false); 873 } 874 875 /** 876 * Called when a virtual view's visibility changed. 877 * 878 * @param view {@link View} that was exited. 879 * @param virtualId id identifying the virtual child inside the parent view. 880 * @param isVisible visible if the view is visible in the view hierarchy. 881 */ 882 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) { 883 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true); 884 } 885 886 /** 887 * Called when a view/virtual view's visibility changed. 888 * 889 * @param view {@link View} that was exited. 890 * @param virtualId id identifying the virtual child inside the parent view. 891 * @param isVisible visible if the view is visible in the view hierarchy. 892 * @param virtual Whether the view is virtual. 893 */ 894 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId, 895 boolean isVisible, boolean virtual) { 896 synchronized (mLock) { 897 if (mEnabled && isActiveLocked()) { 898 final AutofillId id = virtual ? getAutofillId(view, virtualId) 899 : view.getAutofillId(); 900 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible); 901 if (!isVisible && mFillableIds != null) { 902 if (mFillableIds.contains(id)) { 903 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible"); 904 requestHideFillUi(id, view); 905 } 906 } 907 if (mTrackedViews != null) { 908 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible); 909 } else if (sVerbose) { 910 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views"); 911 } 912 } 913 } 914 } 915 916 /** 917 * Called when a virtual view that supports autofill is entered. 918 * 919 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 920 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas, 921 * the absolute bounds could be calculated by: 922 * 923 * <pre class="prettyprint"> 924 * int offset[] = new int[2]; 925 * getLocationOnScreen(offset); 926 * Rect absBounds = new Rect(bounds.left + offset[0], 927 * bounds.top + offset[1], 928 * bounds.right + offset[0], bounds.bottom + offset[1]); 929 * </pre> 930 * 931 * @param view the virtual view parent. 932 * @param virtualId id identifying the virtual child inside the parent view. 933 * @param absBounds absolute boundaries of the virtual view in the screen. 934 */ 935 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 936 notifyViewEntered(view, virtualId, absBounds, 0); 937 } 938 939 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) { 940 if (!hasAutofillFeature()) { 941 return; 942 } 943 AutofillCallback callback; 944 synchronized (mLock) { 945 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags); 946 } 947 948 if (callback != null) { 949 callback.onAutofillEvent(view, virtualId, 950 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 951 } 952 } 953 954 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ 955 @GuardedBy("mLock") 956 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds, 957 int flags) { 958 final AutofillId id = getAutofillId(view, virtualId); 959 AutofillCallback callback = null; 960 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback; 961 962 ensureServiceClientAddedIfNeededLocked(); 963 964 if (!mEnabled) { 965 if (mCallback != null) { 966 callback = mCallback; 967 } 968 } else { 969 // don't notify entered when Activity is already in background 970 if (!isClientDisablingEnterExitEvent()) { 971 if (!isActiveLocked()) { 972 // Starts new session. 973 startSessionLocked(id, bounds, null, flags); 974 } else { 975 // Update focus on existing session. 976 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); 977 } 978 addEnteredIdLocked(id); 979 } 980 } 981 return callback; 982 } 983 984 @GuardedBy("mLock") 985 private void addEnteredIdLocked(@NonNull AutofillId id) { 986 if (mEnteredIds == null) { 987 mEnteredIds = new ArraySet<>(1); 988 } 989 mEnteredIds.add(id); 990 } 991 992 /** 993 * Called when a virtual view that supports autofill is exited. 994 * 995 * @param view the virtual view parent. 996 * @param virtualId id identifying the virtual child inside the parent view. 997 */ 998 public void notifyViewExited(@NonNull View view, int virtualId) { 999 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId); 1000 if (!hasAutofillFeature()) { 1001 return; 1002 } 1003 synchronized (mLock) { 1004 notifyViewExitedLocked(view, virtualId); 1005 } 1006 } 1007 1008 @GuardedBy("mLock") 1009 private void notifyViewExitedLocked(@NonNull View view, int virtualId) { 1010 ensureServiceClientAddedIfNeededLocked(); 1011 1012 if (mEnabled && isActiveLocked()) { 1013 // don't notify exited when Activity is already in background 1014 if (!isClientDisablingEnterExitEvent()) { 1015 final AutofillId id = getAutofillId(view, virtualId); 1016 1017 // Update focus on existing session. 1018 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 1019 } 1020 } 1021 } 1022 1023 /** 1024 * Called to indicate the value of an autofillable {@link View} changed. 1025 * 1026 * @param view view whose value changed. 1027 */ 1028 public void notifyValueChanged(View view) { 1029 if (!hasAutofillFeature()) { 1030 return; 1031 } 1032 AutofillId id = null; 1033 boolean valueWasRead = false; 1034 AutofillValue value = null; 1035 1036 synchronized (mLock) { 1037 // If the session is gone some fields might still be highlighted, hence we have to 1038 // remove the isAutofilled property even if no sessions are active. 1039 if (mLastAutofilledData == null) { 1040 view.setAutofilled(false); 1041 } else { 1042 id = view.getAutofillId(); 1043 if (mLastAutofilledData.containsKey(id)) { 1044 value = view.getAutofillValue(); 1045 valueWasRead = true; 1046 1047 if (Objects.equals(mLastAutofilledData.get(id), value)) { 1048 view.setAutofilled(true); 1049 } else { 1050 view.setAutofilled(false); 1051 mLastAutofilledData.remove(id); 1052 } 1053 } else { 1054 view.setAutofilled(false); 1055 } 1056 } 1057 1058 if (!mEnabled || !isActiveLocked()) { 1059 if (sVerbose) { 1060 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() 1061 + "): ignoring on state " + getStateAsStringLocked()); 1062 } 1063 return; 1064 } 1065 1066 if (id == null) { 1067 id = view.getAutofillId(); 1068 } 1069 1070 if (!valueWasRead) { 1071 value = view.getAutofillValue(); 1072 } 1073 1074 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 1075 } 1076 } 1077 1078 /** 1079 * Called to indicate the value of an autofillable virtual view has changed. 1080 * 1081 * @param view the virtual view parent. 1082 * @param virtualId id identifying the virtual child inside the parent view. 1083 * @param value new value of the child. 1084 */ 1085 public void notifyValueChanged(View view, int virtualId, AutofillValue value) { 1086 if (!hasAutofillFeature()) { 1087 return; 1088 } 1089 synchronized (mLock) { 1090 if (!mEnabled || !isActiveLocked()) { 1091 if (sVerbose) { 1092 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId 1093 + "): ignoring on state " + getStateAsStringLocked()); 1094 } 1095 return; 1096 } 1097 1098 final AutofillId id = getAutofillId(view, virtualId); 1099 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 1100 } 1101 } 1102 1103 /** 1104 * Called to indicate a {@link View} is clicked. 1105 * 1106 * @param view view that has been clicked. 1107 */ 1108 public void notifyViewClicked(@NonNull View view) { 1109 notifyViewClicked(view.getAutofillId()); 1110 } 1111 1112 /** 1113 * Called to indicate a virtual view has been clicked. 1114 * 1115 * @param view the virtual view parent. 1116 * @param virtualId id identifying the virtual child inside the parent view. 1117 */ 1118 public void notifyViewClicked(@NonNull View view, int virtualId) { 1119 notifyViewClicked(getAutofillId(view, virtualId)); 1120 } 1121 1122 private void notifyViewClicked(AutofillId id) { 1123 if (!hasAutofillFeature()) { 1124 return; 1125 } 1126 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId); 1127 1128 synchronized (mLock) { 1129 if (!mEnabled || !isActiveLocked()) { 1130 return; 1131 } 1132 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) { 1133 if (sDebug) Log.d(TAG, "triggering commit by click of " + id); 1134 commitLocked(); 1135 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED, 1136 mContext.getPackageName()); 1137 } 1138 } 1139 } 1140 1141 /** 1142 * Called by {@link android.app.Activity} to commit or cancel the session on finish. 1143 * 1144 * @hide 1145 */ 1146 public void onActivityFinishing() { 1147 if (!hasAutofillFeature()) { 1148 return; 1149 } 1150 synchronized (mLock) { 1151 if (mSaveOnFinish) { 1152 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()"); 1153 commitLocked(); 1154 } else { 1155 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()"); 1156 cancelLocked(); 1157 } 1158 } 1159 } 1160 1161 /** 1162 * Called to indicate the current autofill context should be commited. 1163 * 1164 * <p>This method is typically called by {@link View Views} that manage virtual views; for 1165 * example, when the view is rendering an {@code HTML} page with a form and virtual views 1166 * that represent the HTML elements, it should call this method after the form is submitted and 1167 * another page is rendered. 1168 * 1169 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle 1170 * methods such as {@link android.app.Activity#finish()}. 1171 */ 1172 public void commit() { 1173 if (!hasAutofillFeature()) { 1174 return; 1175 } 1176 if (sVerbose) Log.v(TAG, "commit() called by app"); 1177 synchronized (mLock) { 1178 commitLocked(); 1179 } 1180 } 1181 1182 @GuardedBy("mLock") 1183 private void commitLocked() { 1184 if (!mEnabled && !isActiveLocked()) { 1185 return; 1186 } 1187 finishSessionLocked(); 1188 } 1189 1190 /** 1191 * Called to indicate the current autofill context should be cancelled. 1192 * 1193 * <p>This method is typically called by {@link View Views} that manage virtual views; for 1194 * example, when the view is rendering an {@code HTML} page with a form and virtual views 1195 * that represent the HTML elements, it should call this method if the user does not post the 1196 * form but moves to another form in this page. 1197 * 1198 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle 1199 * methods such as {@link android.app.Activity#finish()}. 1200 */ 1201 public void cancel() { 1202 if (sVerbose) Log.v(TAG, "cancel() called by app"); 1203 if (!hasAutofillFeature()) { 1204 return; 1205 } 1206 synchronized (mLock) { 1207 cancelLocked(); 1208 } 1209 } 1210 1211 @GuardedBy("mLock") 1212 private void cancelLocked() { 1213 if (!mEnabled && !isActiveLocked()) { 1214 return; 1215 } 1216 cancelSessionLocked(); 1217 } 1218 1219 /** @hide */ 1220 public void disableOwnedAutofillServices() { 1221 disableAutofillServices(); 1222 } 1223 1224 /** 1225 * If the app calling this API has enabled autofill services they 1226 * will be disabled. 1227 */ 1228 public void disableAutofillServices() { 1229 if (!hasAutofillFeature()) { 1230 return; 1231 } 1232 try { 1233 mService.disableOwnedAutofillServices(mContext.getUserId()); 1234 } catch (RemoteException e) { 1235 throw e.rethrowFromSystemServer(); 1236 } 1237 } 1238 1239 /** 1240 * Returns {@code true} if the calling application provides a {@link AutofillService} that is 1241 * enabled for the current user, or {@code false} otherwise. 1242 */ 1243 public boolean hasEnabledAutofillServices() { 1244 if (mService == null) return false; 1245 1246 try { 1247 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName()); 1248 } catch (RemoteException e) { 1249 throw e.rethrowFromSystemServer(); 1250 } 1251 } 1252 1253 /** 1254 * Returns the component name of the {@link AutofillService} that is enabled for the current 1255 * user. 1256 */ 1257 @Nullable 1258 public ComponentName getAutofillServiceComponentName() { 1259 if (mService == null) return null; 1260 1261 try { 1262 return mService.getAutofillServiceComponentName(); 1263 } catch (RemoteException e) { 1264 throw e.rethrowFromSystemServer(); 1265 } 1266 } 1267 1268 /** 1269 * Gets the id of the {@link UserData} used for 1270 * <a href="AutofillService.html#FieldClassification">field classification</a>. 1271 * 1272 * <p>This method is useful when the service must check the status of the {@link UserData} in 1273 * the device without fetching the whole object. 1274 * 1275 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1276 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1277 * the user. 1278 * 1279 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)} 1280 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill 1281 * service for the user. 1282 */ 1283 @Nullable public String getUserDataId() { 1284 try { 1285 return mService.getUserDataId(); 1286 } catch (RemoteException e) { 1287 e.rethrowFromSystemServer(); 1288 return null; 1289 } 1290 } 1291 1292 /** 1293 * Gets the user data used for 1294 * <a href="AutofillService.html#FieldClassification">field classification</a>. 1295 * 1296 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1297 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1298 * the user. 1299 * 1300 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was 1301 * reset or if the caller currently does not have an enabled autofill service for the user. 1302 */ 1303 @Nullable public UserData getUserData() { 1304 try { 1305 return mService.getUserData(); 1306 } catch (RemoteException e) { 1307 e.rethrowFromSystemServer(); 1308 return null; 1309 } 1310 } 1311 1312 /** 1313 * Sets the {@link UserData} used for 1314 * <a href="AutofillService.html#FieldClassification">field classification</a> 1315 * 1316 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1317 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1318 * the user. 1319 */ 1320 public void setUserData(@Nullable UserData userData) { 1321 try { 1322 mService.setUserData(userData); 1323 } catch (RemoteException e) { 1324 e.rethrowFromSystemServer(); 1325 } 1326 } 1327 1328 /** 1329 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is 1330 * enabled. 1331 * 1332 * <p>As field classification is an expensive operation, it could be disabled, either 1333 * temporarily (for example, because the service exceeded a rate-limit threshold) or 1334 * permanently (for example, because the device is a low-level device). 1335 * 1336 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1337 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1338 * the user. 1339 */ 1340 public boolean isFieldClassificationEnabled() { 1341 try { 1342 return mService.isFieldClassificationEnabled(); 1343 } catch (RemoteException e) { 1344 e.rethrowFromSystemServer(); 1345 return false; 1346 } 1347 } 1348 1349 /** 1350 * Gets the name of the default algorithm used for 1351 * <a href="AutofillService.html#FieldClassification">field classification</a>. 1352 * 1353 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not 1354 * set. 1355 * 1356 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1357 * and it's ignored if the caller currently doesn't have an enabled autofill service for 1358 * the user. 1359 */ 1360 @Nullable 1361 public String getDefaultFieldClassificationAlgorithm() { 1362 try { 1363 return mService.getDefaultFieldClassificationAlgorithm(); 1364 } catch (RemoteException e) { 1365 e.rethrowFromSystemServer(); 1366 return null; 1367 } 1368 } 1369 1370 /** 1371 * Gets the name of all algorithms currently available for 1372 * <a href="AutofillService.html#FieldClassification">field classification</a>. 1373 * 1374 * <p><b>Note:</b> This method should only be called by an app providing an autofill service, 1375 * and it returns an empty list if the caller currently doesn't have an enabled autofill service 1376 * for the user. 1377 */ 1378 @NonNull 1379 public List<String> getAvailableFieldClassificationAlgorithms() { 1380 final String[] algorithms; 1381 try { 1382 algorithms = mService.getAvailableFieldClassificationAlgorithms(); 1383 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList(); 1384 } catch (RemoteException e) { 1385 e.rethrowFromSystemServer(); 1386 return null; 1387 } 1388 } 1389 1390 /** 1391 * Returns {@code true} if autofill is supported by the current device and 1392 * is supported for this user. 1393 * 1394 * <p>Autofill is typically supported, but it could be unsupported in cases like: 1395 * <ol> 1396 * <li>Low-end devices. 1397 * <li>Device policy rules that forbid its usage. 1398 * </ol> 1399 */ 1400 public boolean isAutofillSupported() { 1401 if (mService == null) return false; 1402 1403 try { 1404 return mService.isServiceSupported(mContext.getUserId()); 1405 } catch (RemoteException e) { 1406 throw e.rethrowFromSystemServer(); 1407 } 1408 } 1409 1410 // Note: don't need to use locked suffix because mContext is final. 1411 private AutofillClient getClient() { 1412 final AutofillClient client = mContext.getAutofillClient(); 1413 if (client == null && sDebug) { 1414 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context " 1415 + mContext); 1416 } 1417 return client; 1418 } 1419 1420 /** 1421 * Check if autofill ui is showing, must be called on UI thread. 1422 * @hide 1423 */ 1424 public boolean isAutofillUiShowing() { 1425 final AutofillClient client = mContext.getAutofillClient(); 1426 return client != null && client.autofillClientIsFillUiShowing(); 1427 } 1428 1429 /** @hide */ 1430 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) { 1431 if (!hasAutofillFeature()) { 1432 return; 1433 } 1434 // TODO: the result code is being ignored, so this method is not reliably 1435 // handling the cases where it's not RESULT_OK: it works fine if the service does not 1436 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the 1437 // service set the extra and returned RESULT_CANCELED... 1438 1439 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data); 1440 1441 synchronized (mLock) { 1442 if (!isActiveLocked()) { 1443 return; 1444 } 1445 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart 1446 // of app activity. We enforce enter event to re-show fill ui in such case. 1447 // CTS example: 1448 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt 1449 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt 1450 if (!mOnInvisibleCalled && focusView != null 1451 && focusView.canNotifyAutofillEnterExitEvent()) { 1452 notifyViewExitedLocked(focusView); 1453 notifyViewEnteredLocked(focusView, 0); 1454 } 1455 if (data == null) { 1456 // data is set to null when result is not RESULT_OK 1457 return; 1458 } 1459 1460 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT); 1461 final Bundle responseData = new Bundle(); 1462 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); 1463 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE); 1464 if (newClientState != null) { 1465 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState); 1466 } 1467 try { 1468 mService.setAuthenticationResult(responseData, mSessionId, authenticationId, 1469 mContext.getUserId()); 1470 } catch (RemoteException e) { 1471 Log.e(TAG, "Error delivering authentication result", e); 1472 } 1473 } 1474 } 1475 1476 /** 1477 * Gets the next unique autofill ID for the activity context. 1478 * 1479 * <p>Typically used to manage views whose content is recycled - see 1480 * {@link View#setAutofillId(AutofillId)} for more info. 1481 * 1482 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in 1483 * the {@link Context} associated with this {@link AutofillManager}. 1484 */ 1485 @Nullable 1486 public AutofillId getNextAutofillId() { 1487 final AutofillClient client = getClient(); 1488 if (client == null) return null; 1489 1490 final AutofillId id = client.autofillClientGetNextAutofillId(); 1491 1492 if (id == null && sDebug) { 1493 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null"); 1494 } 1495 1496 return id; 1497 } 1498 1499 private static AutofillId getAutofillId(View parent, int virtualId) { 1500 return new AutofillId(parent.getAutofillViewId(), virtualId); 1501 } 1502 1503 @GuardedBy("mLock") 1504 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, 1505 @NonNull AutofillValue value, int flags) { 1506 if (sVerbose) { 1507 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value 1508 + ", flags=" + flags + ", state=" + getStateAsStringLocked() 1509 + ", compatMode=" + isCompatibilityModeEnabledLocked() 1510 + ", enteredIds=" + mEnteredIds); 1511 } 1512 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) { 1513 if (sVerbose) { 1514 Log.v(TAG, "not automatically starting session for " + id 1515 + " on state " + getStateAsStringLocked() + " and flags " + flags); 1516 } 1517 return; 1518 } 1519 try { 1520 final AutofillClient client = getClient(); 1521 if (client == null) return; // NOTE: getClient() already logged it.. 1522 1523 mSessionId = mService.startSession(client.autofillClientGetActivityToken(), 1524 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 1525 mCallback != null, flags, client.autofillClientGetComponentName(), 1526 isCompatibilityModeEnabledLocked()); 1527 if (mSessionId != NO_SESSION) { 1528 mState = STATE_ACTIVE; 1529 } 1530 client.autofillClientResetableStateAvailable(); 1531 } catch (RemoteException e) { 1532 throw e.rethrowFromSystemServer(); 1533 } 1534 } 1535 1536 @GuardedBy("mLock") 1537 private void finishSessionLocked() { 1538 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked()); 1539 1540 if (!isActiveLocked()) return; 1541 1542 try { 1543 mService.finishSession(mSessionId, mContext.getUserId()); 1544 } catch (RemoteException e) { 1545 throw e.rethrowFromSystemServer(); 1546 } 1547 1548 resetSessionLocked(/* resetEnteredIds= */ true); 1549 } 1550 1551 @GuardedBy("mLock") 1552 private void cancelSessionLocked() { 1553 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked()); 1554 1555 if (!isActiveLocked()) return; 1556 1557 try { 1558 mService.cancelSession(mSessionId, mContext.getUserId()); 1559 } catch (RemoteException e) { 1560 throw e.rethrowFromSystemServer(); 1561 } 1562 1563 resetSessionLocked(/* resetEnteredIds= */ true); 1564 } 1565 1566 @GuardedBy("mLock") 1567 private void resetSessionLocked(boolean resetEnteredIds) { 1568 mSessionId = NO_SESSION; 1569 mState = STATE_UNKNOWN; 1570 mTrackedViews = null; 1571 mFillableIds = null; 1572 mSaveTriggerId = null; 1573 mIdShownFillUi = null; 1574 if (resetEnteredIds) { 1575 mEnteredIds = null; 1576 } 1577 } 1578 1579 @GuardedBy("mLock") 1580 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, 1581 int flags) { 1582 if (sVerbose) { 1583 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds 1584 + ", value=" + value + ", action=" + action + ", flags=" + flags); 1585 } 1586 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0; 1587 1588 try { 1589 if (restartIfNecessary) { 1590 final AutofillClient client = getClient(); 1591 if (client == null) return; // NOTE: getClient() already logd it.. 1592 1593 final int newId = mService.updateOrRestartSession( 1594 client.autofillClientGetActivityToken(), 1595 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 1596 mCallback != null, flags, client.autofillClientGetComponentName(), 1597 mSessionId, action, isCompatibilityModeEnabledLocked()); 1598 if (newId != mSessionId) { 1599 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); 1600 mSessionId = newId; 1601 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE; 1602 client.autofillClientResetableStateAvailable(); 1603 } 1604 } else { 1605 mService.updateSession(mSessionId, id, bounds, value, action, flags, 1606 mContext.getUserId()); 1607 } 1608 1609 } catch (RemoteException e) { 1610 throw e.rethrowFromSystemServer(); 1611 } 1612 } 1613 1614 @GuardedBy("mLock") 1615 private void ensureServiceClientAddedIfNeededLocked() { 1616 if (getClient() == null) { 1617 return; 1618 } 1619 1620 if (mServiceClient == null) { 1621 mServiceClient = new AutofillManagerClient(this); 1622 try { 1623 final int userId = mContext.getUserId(); 1624 final int flags = mService.addClient(mServiceClient, userId); 1625 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; 1626 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0; 1627 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0; 1628 final IAutoFillManager service = mService; 1629 final IAutoFillManagerClient serviceClient = mServiceClient; 1630 mServiceClientCleaner = Cleaner.create(this, () -> { 1631 try { 1632 service.removeClient(serviceClient, userId); 1633 } catch (RemoteException e) { 1634 } 1635 }); 1636 } catch (RemoteException e) { 1637 throw e.rethrowFromSystemServer(); 1638 } 1639 } 1640 } 1641 1642 /** 1643 * Registers a {@link AutofillCallback} to receive autofill events. 1644 * 1645 * @param callback callback to receive events. 1646 */ 1647 public void registerCallback(@Nullable AutofillCallback callback) { 1648 if (!hasAutofillFeature()) { 1649 return; 1650 } 1651 synchronized (mLock) { 1652 if (callback == null) return; 1653 1654 final boolean hadCallback = mCallback != null; 1655 mCallback = callback; 1656 1657 if (!hadCallback) { 1658 try { 1659 mService.setHasCallback(mSessionId, mContext.getUserId(), true); 1660 } catch (RemoteException e) { 1661 throw e.rethrowFromSystemServer(); 1662 } 1663 } 1664 } 1665 } 1666 1667 /** 1668 * Unregisters a {@link AutofillCallback} to receive autofill events. 1669 * 1670 * @param callback callback to stop receiving events. 1671 */ 1672 public void unregisterCallback(@Nullable AutofillCallback callback) { 1673 if (!hasAutofillFeature()) { 1674 return; 1675 } 1676 synchronized (mLock) { 1677 if (callback == null || mCallback == null || callback != mCallback) return; 1678 1679 mCallback = null; 1680 1681 try { 1682 mService.setHasCallback(mSessionId, mContext.getUserId(), false); 1683 } catch (RemoteException e) { 1684 throw e.rethrowFromSystemServer(); 1685 } 1686 } 1687 } 1688 1689 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 1690 Rect anchorBounds, IAutofillWindowPresenter presenter) { 1691 final View anchor = findView(id); 1692 if (anchor == null) { 1693 return; 1694 } 1695 1696 AutofillCallback callback = null; 1697 synchronized (mLock) { 1698 if (mSessionId == sessionId) { 1699 AutofillClient client = getClient(); 1700 1701 if (client != null) { 1702 if (client.autofillClientRequestShowFillUi(anchor, width, height, 1703 anchorBounds, presenter)) { 1704 callback = mCallback; 1705 mIdShownFillUi = id; 1706 } 1707 } 1708 } 1709 } 1710 1711 if (callback != null) { 1712 if (id.isVirtual()) { 1713 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1714 AutofillCallback.EVENT_INPUT_SHOWN); 1715 } else { 1716 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); 1717 } 1718 } 1719 } 1720 1721 private void authenticate(int sessionId, int authenticationId, IntentSender intent, 1722 Intent fillInIntent) { 1723 synchronized (mLock) { 1724 if (sessionId == mSessionId) { 1725 final AutofillClient client = getClient(); 1726 if (client != null) { 1727 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill() 1728 // before onAuthenticationResult() 1729 mOnInvisibleCalled = false; 1730 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent); 1731 } 1732 } 1733 } 1734 } 1735 1736 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) { 1737 final View anchor = findView(id); 1738 if (anchor == null) { 1739 return; 1740 } 1741 1742 AutofillCallback callback = null; 1743 synchronized (mLock) { 1744 if (mSessionId == sessionId) { 1745 AutofillClient client = getClient(); 1746 1747 if (client != null) { 1748 client.autofillClientDispatchUnhandledKey(anchor, keyEvent); 1749 } 1750 } 1751 } 1752 } 1753 1754 /** @hide */ 1755 public static final int SET_STATE_FLAG_ENABLED = 0x01; 1756 /** @hide */ 1757 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02; 1758 /** @hide */ 1759 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04; 1760 /** @hide */ 1761 public static final int SET_STATE_FLAG_DEBUG = 0x08; 1762 /** @hide */ 1763 public static final int SET_STATE_FLAG_VERBOSE = 0x10; 1764 1765 private void setState(int flags) { 1766 if (sVerbose) Log.v(TAG, "setState(" + flags + ")"); 1767 synchronized (mLock) { 1768 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0; 1769 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) { 1770 // Reset the session state 1771 resetSessionLocked(/* resetEnteredIds= */ true); 1772 } 1773 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) { 1774 // Reset connection to system 1775 mServiceClient = null; 1776 if (mServiceClientCleaner != null) { 1777 mServiceClientCleaner.clean(); 1778 mServiceClientCleaner = null; 1779 } 1780 } 1781 } 1782 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0; 1783 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0; 1784 } 1785 1786 /** 1787 * Sets a view as autofilled if the current value is the {code targetValue}. 1788 * 1789 * @param view The view that is to be autofilled 1790 * @param targetValue The value we want to fill into view 1791 */ 1792 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { 1793 AutofillValue currentValue = view.getAutofillValue(); 1794 if (Objects.equals(currentValue, targetValue)) { 1795 synchronized (mLock) { 1796 if (mLastAutofilledData == null) { 1797 mLastAutofilledData = new ParcelableMap(1); 1798 } 1799 mLastAutofilledData.put(view.getAutofillId(), targetValue); 1800 } 1801 view.setAutofilled(true); 1802 } 1803 } 1804 1805 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 1806 synchronized (mLock) { 1807 if (sessionId != mSessionId) { 1808 return; 1809 } 1810 1811 final AutofillClient client = getClient(); 1812 if (client == null) { 1813 return; 1814 } 1815 1816 final int itemCount = ids.size(); 1817 int numApplied = 0; 1818 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; 1819 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal( 1820 Helper.toArray(ids)); 1821 1822 for (int i = 0; i < itemCount; i++) { 1823 final AutofillId id = ids.get(i); 1824 final AutofillValue value = values.get(i); 1825 final int viewId = id.getViewId(); 1826 final View view = views[i]; 1827 if (view == null) { 1828 Log.w(TAG, "autofill(): no View with id " + viewId); 1829 continue; 1830 } 1831 if (id.isVirtual()) { 1832 if (virtualValues == null) { 1833 // Most likely there will be just one view with virtual children. 1834 virtualValues = new ArrayMap<>(1); 1835 } 1836 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); 1837 if (valuesByParent == null) { 1838 // We don't know the size yet, but usually it will be just a few fields... 1839 valuesByParent = new SparseArray<>(5); 1840 virtualValues.put(view, valuesByParent); 1841 } 1842 valuesByParent.put(id.getVirtualChildId(), value); 1843 } else { 1844 // Mark the view as to be autofilled with 'value' 1845 if (mLastAutofilledData == null) { 1846 mLastAutofilledData = new ParcelableMap(itemCount - i); 1847 } 1848 mLastAutofilledData.put(id, value); 1849 1850 view.autofill(value); 1851 1852 // Set as autofilled if the values match now, e.g. when the value was updated 1853 // synchronously. 1854 // If autofill happens async, the view is set to autofilled in 1855 // notifyValueChanged. 1856 setAutofilledIfValuesIs(view, value); 1857 1858 numApplied++; 1859 } 1860 } 1861 1862 if (virtualValues != null) { 1863 for (int i = 0; i < virtualValues.size(); i++) { 1864 final View parent = virtualValues.keyAt(i); 1865 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); 1866 parent.autofill(childrenValues); 1867 numApplied += childrenValues.size(); 1868 } 1869 } 1870 1871 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED) 1872 .setPackageName(mContext.getPackageName()) 1873 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount) 1874 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); 1875 mMetricsLogger.write(log); 1876 } 1877 } 1878 1879 /** 1880 * Set the tracked views. 1881 * 1882 * @param trackedIds The views to be tracked. 1883 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible. 1884 * @param saveOnFinish Finish the session once the activity is finished. 1885 * @param fillableIds Views that might anchor FillUI. 1886 * @param saveTriggerId View that when clicked triggers commit(). 1887 */ 1888 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, 1889 boolean saveOnAllViewsInvisible, boolean saveOnFinish, 1890 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { 1891 synchronized (mLock) { 1892 if (mEnabled && mSessionId == sessionId) { 1893 if (saveOnAllViewsInvisible) { 1894 mTrackedViews = new TrackedViews(trackedIds); 1895 } else { 1896 mTrackedViews = null; 1897 } 1898 mSaveOnFinish = saveOnFinish; 1899 if (fillableIds != null) { 1900 if (mFillableIds == null) { 1901 mFillableIds = new ArraySet<>(fillableIds.length); 1902 } 1903 for (AutofillId id : fillableIds) { 1904 mFillableIds.add(id); 1905 } 1906 if (sVerbose) { 1907 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds 1908 + ", mFillableIds" + mFillableIds); 1909 } 1910 } 1911 1912 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) { 1913 // Turn off trigger on previous view id. 1914 setNotifyOnClickLocked(mSaveTriggerId, false); 1915 } 1916 1917 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) { 1918 // Turn on trigger on new view id. 1919 mSaveTriggerId = saveTriggerId; 1920 setNotifyOnClickLocked(mSaveTriggerId, true); 1921 } 1922 } 1923 } 1924 } 1925 1926 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) { 1927 final View view = findView(id); 1928 if (view == null) { 1929 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id); 1930 return; 1931 } 1932 view.setNotifyAutofillManagerOnClick(notify); 1933 } 1934 1935 private void setSaveUiState(int sessionId, boolean shown) { 1936 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown); 1937 synchronized (mLock) { 1938 if (mSessionId != NO_SESSION) { 1939 // Race condition: app triggered a new session after the previous session was 1940 // finished but before server called setSaveUiState() - need to cancel the new 1941 // session to avoid further inconsistent behavior. 1942 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown 1943 + ") called on existing session " + mSessionId + "; cancelling it"); 1944 cancelSessionLocked(); 1945 } 1946 if (shown) { 1947 mSessionId = sessionId; 1948 mState = STATE_SHOWING_SAVE_UI; 1949 } else { 1950 mSessionId = NO_SESSION; 1951 mState = STATE_UNKNOWN; 1952 } 1953 } 1954 } 1955 1956 /** 1957 * Marks the state of the session as finished. 1958 * 1959 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null} 1960 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), 1961 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar 1962 * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service 1963 * disabled further autofill requests for the activity). 1964 */ 1965 private void setSessionFinished(int newState) { 1966 synchronized (mLock) { 1967 if (sVerbose) { 1968 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to " 1969 + getStateAsString(newState)); 1970 } 1971 if (newState == STATE_UNKNOWN_COMPAT_MODE) { 1972 resetSessionLocked(/* resetEnteredIds= */ true); 1973 mState = STATE_UNKNOWN; 1974 } else { 1975 resetSessionLocked(/* resetEnteredIds= */ false); 1976 mState = newState; 1977 } 1978 } 1979 } 1980 1981 /** @hide */ 1982 public void requestHideFillUi() { 1983 requestHideFillUi(mIdShownFillUi, true); 1984 } 1985 1986 private void requestHideFillUi(AutofillId id, boolean force) { 1987 final View anchor = id == null ? null : findView(id); 1988 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor); 1989 if (anchor == null) { 1990 if (force) { 1991 // When user taps outside autofill window, force to close fill ui even id does 1992 // not match. 1993 AutofillClient client = getClient(); 1994 if (client != null) { 1995 client.autofillClientRequestHideFillUi(); 1996 } 1997 } 1998 return; 1999 } 2000 requestHideFillUi(id, anchor); 2001 } 2002 2003 private void requestHideFillUi(AutofillId id, View anchor) { 2004 2005 AutofillCallback callback = null; 2006 synchronized (mLock) { 2007 // We do not check the session id for two reasons: 2008 // 1. If local and remote session id are off sync the UI would be stuck shown 2009 // 2. There is a race between the user state being destroyed due the fill 2010 // service being uninstalled and the UI being dismissed. 2011 AutofillClient client = getClient(); 2012 if (client != null) { 2013 if (client.autofillClientRequestHideFillUi()) { 2014 mIdShownFillUi = null; 2015 callback = mCallback; 2016 } 2017 } 2018 } 2019 2020 if (callback != null) { 2021 if (id.isVirtual()) { 2022 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 2023 AutofillCallback.EVENT_INPUT_HIDDEN); 2024 } else { 2025 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); 2026 } 2027 } 2028 } 2029 2030 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) { 2031 if (sVerbose) { 2032 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id 2033 + ", sessionFinishedState=" + sessionFinishedState); 2034 } 2035 final View anchor = findView(id); 2036 if (anchor == null) { 2037 return; 2038 } 2039 2040 AutofillCallback callback = null; 2041 synchronized (mLock) { 2042 if (mSessionId == sessionId && getClient() != null) { 2043 callback = mCallback; 2044 } 2045 } 2046 2047 if (callback != null) { 2048 if (id.isVirtual()) { 2049 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 2050 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 2051 } else { 2052 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 2053 } 2054 } 2055 2056 if (sessionFinishedState != 0) { 2057 // Callback call was "hijacked" to also update the session state. 2058 setSessionFinished(sessionFinishedState); 2059 } 2060 } 2061 2062 /** 2063 * Find a single view by its id. 2064 * 2065 * @param autofillId The autofill id of the view 2066 * 2067 * @return The view or {@code null} if view was not found 2068 */ 2069 private View findView(@NonNull AutofillId autofillId) { 2070 final AutofillClient client = getClient(); 2071 if (client != null) { 2072 return client.autofillClientFindViewByAutofillIdTraversal(autofillId); 2073 } 2074 return null; 2075 } 2076 2077 /** @hide */ 2078 public boolean hasAutofillFeature() { 2079 return mService != null; 2080 } 2081 2082 /** @hide */ 2083 public void onPendingSaveUi(int operation, IBinder token) { 2084 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 2085 2086 synchronized (mLock) { 2087 try { 2088 mService.onPendingSaveUi(operation, token); 2089 } catch (RemoteException e) { 2090 e.rethrowFromSystemServer(); 2091 } 2092 } 2093 } 2094 2095 /** @hide */ 2096 public void dump(String outerPrefix, PrintWriter pw) { 2097 pw.print(outerPrefix); pw.println("AutofillManager:"); 2098 final String pfx = outerPrefix + " "; 2099 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId); 2100 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked()); 2101 pw.print(pfx); pw.print("context: "); pw.println(mContext); 2102 pw.print(pfx); pw.print("client: "); pw.println(getClient()); 2103 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled); 2104 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null); 2105 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null); 2106 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled); 2107 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData); 2108 pw.print(pfx); pw.print("tracked views: "); 2109 if (mTrackedViews == null) { 2110 pw.println("null"); 2111 } else { 2112 final String pfx2 = pfx + " "; 2113 pw.println(); 2114 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds); 2115 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds); 2116 } 2117 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds); 2118 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds); 2119 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); 2120 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); 2121 pw.print(pfx); pw.print("compat mode enabled: "); pw.println( 2122 isCompatibilityModeEnabledLocked()); 2123 pw.print(pfx); pw.print("debug: "); pw.print(sDebug); 2124 pw.print(" verbose: "); pw.println(sVerbose); 2125 } 2126 2127 @GuardedBy("mLock") 2128 private String getStateAsStringLocked() { 2129 return getStateAsString(mState); 2130 } 2131 2132 @NonNull 2133 private static String getStateAsString(int state) { 2134 switch (state) { 2135 case STATE_UNKNOWN: 2136 return "UNKNOWN"; 2137 case STATE_ACTIVE: 2138 return "ACTIVE"; 2139 case STATE_FINISHED: 2140 return "FINISHED"; 2141 case STATE_SHOWING_SAVE_UI: 2142 return "SHOWING_SAVE_UI"; 2143 case STATE_DISABLED_BY_SERVICE: 2144 return "DISABLED_BY_SERVICE"; 2145 case STATE_UNKNOWN_COMPAT_MODE: 2146 return "UNKNOWN_COMPAT_MODE"; 2147 default: 2148 return "INVALID:" + state; 2149 } 2150 } 2151 2152 @GuardedBy("mLock") 2153 private boolean isActiveLocked() { 2154 return mState == STATE_ACTIVE; 2155 } 2156 2157 @GuardedBy("mLock") 2158 private boolean isDisabledByServiceLocked() { 2159 return mState == STATE_DISABLED_BY_SERVICE; 2160 } 2161 2162 @GuardedBy("mLock") 2163 private boolean isFinishedLocked() { 2164 return mState == STATE_FINISHED; 2165 } 2166 2167 private void post(Runnable runnable) { 2168 final AutofillClient client = getClient(); 2169 if (client == null) { 2170 if (sVerbose) Log.v(TAG, "ignoring post() because client is null"); 2171 return; 2172 } 2173 client.autofillClientRunOnUiThread(runnable); 2174 } 2175 2176 /** 2177 * Implementation of the accessibility based compatibility. 2178 */ 2179 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy { 2180 @GuardedBy("mLock") 2181 private final Rect mFocusedBounds = new Rect(); 2182 @GuardedBy("mLock") 2183 private final Rect mTempBounds = new Rect(); 2184 2185 @GuardedBy("mLock") 2186 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 2187 @GuardedBy("mLock") 2188 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; 2189 2190 // Need to report a fake service in case a11y clients check the service list 2191 @NonNull 2192 @GuardedBy("mLock") 2193 AccessibilityServiceInfo mCompatServiceInfo; 2194 2195 CompatibilityBridge() { 2196 final AccessibilityManager am = AccessibilityManager.getInstance(mContext); 2197 am.setAccessibilityPolicy(this); 2198 } 2199 2200 private AccessibilityServiceInfo getCompatServiceInfo() { 2201 synchronized (mLock) { 2202 if (mCompatServiceInfo != null) { 2203 return mCompatServiceInfo; 2204 } 2205 final Intent intent = new Intent(); 2206 intent.setComponent(new ComponentName("android", 2207 "com.android.server.autofill.AutofillCompatAccessibilityService")); 2208 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService( 2209 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA); 2210 try { 2211 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext); 2212 } catch (XmlPullParserException | IOException e) { 2213 Log.e(TAG, "Cannot find compat autofill service:" + intent); 2214 throw new IllegalStateException("Cannot find compat autofill service"); 2215 } 2216 return mCompatServiceInfo; 2217 } 2218 } 2219 2220 @Override 2221 public boolean isEnabled(boolean accessibilityEnabled) { 2222 return true; 2223 } 2224 2225 @Override 2226 public int getRelevantEventTypes(int relevantEventTypes) { 2227 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED 2228 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED 2229 | AccessibilityEvent.TYPE_VIEW_CLICKED 2230 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; 2231 } 2232 2233 @Override 2234 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList( 2235 List<AccessibilityServiceInfo> installedServices) { 2236 if (installedServices == null) { 2237 installedServices = new ArrayList<>(); 2238 } 2239 installedServices.add(getCompatServiceInfo()); 2240 return installedServices; 2241 } 2242 2243 @Override 2244 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( 2245 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) { 2246 if (enabledService == null) { 2247 enabledService = new ArrayList<>(); 2248 } 2249 enabledService.add(getCompatServiceInfo()); 2250 return enabledService; 2251 } 2252 2253 @Override 2254 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event, 2255 boolean accessibilityEnabled, int relevantEventTypes) { 2256 switch (event.getEventType()) { 2257 case AccessibilityEvent.TYPE_VIEW_FOCUSED: { 2258 synchronized (mLock) { 2259 if (mFocusedWindowId == event.getWindowId() 2260 && mFocusedNodeId == event.getSourceNodeId()) { 2261 return event; 2262 } 2263 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID 2264 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) { 2265 notifyViewExited(mFocusedWindowId, mFocusedNodeId); 2266 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 2267 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; 2268 mFocusedBounds.set(0, 0, 0, 0); 2269 } 2270 final int windowId = event.getWindowId(); 2271 final long nodeId = event.getSourceNodeId(); 2272 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) { 2273 mFocusedWindowId = windowId; 2274 mFocusedNodeId = nodeId; 2275 } 2276 } 2277 } break; 2278 2279 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: { 2280 synchronized (mLock) { 2281 if (mFocusedWindowId == event.getWindowId() 2282 && mFocusedNodeId == event.getSourceNodeId()) { 2283 notifyValueChanged(event.getWindowId(), event.getSourceNodeId()); 2284 } 2285 } 2286 } break; 2287 2288 case AccessibilityEvent.TYPE_VIEW_CLICKED: { 2289 synchronized (mLock) { 2290 notifyViewClicked(event.getWindowId(), event.getSourceNodeId()); 2291 } 2292 } break; 2293 2294 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { 2295 final AutofillClient client = getClient(); 2296 if (client != null) { 2297 synchronized (mLock) { 2298 if (client.autofillClientIsFillUiShowing()) { 2299 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds); 2300 } 2301 updateTrackedViewsLocked(); 2302 } 2303 } 2304 } break; 2305 } 2306 2307 return accessibilityEnabled ? event : null; 2308 } 2309 2310 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) { 2311 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId); 2312 if (!isVirtualNode(virtualId)) { 2313 return false; 2314 } 2315 final View view = findViewByAccessibilityId(windowId, nodeId); 2316 if (view == null) { 2317 return false; 2318 } 2319 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId); 2320 if (node == null) { 2321 return false; 2322 } 2323 if (!node.isEditable()) { 2324 return false; 2325 } 2326 final Rect newBounds = mTempBounds; 2327 node.getBoundsInScreen(newBounds); 2328 if (newBounds.equals(focusedBounds)) { 2329 return false; 2330 } 2331 focusedBounds.set(newBounds); 2332 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds); 2333 return true; 2334 } 2335 2336 private void notifyViewExited(int windowId, long nodeId) { 2337 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId); 2338 if (!isVirtualNode(virtualId)) { 2339 return; 2340 } 2341 final View view = findViewByAccessibilityId(windowId, nodeId); 2342 if (view == null) { 2343 return; 2344 } 2345 AutofillManager.this.notifyViewExited(view, virtualId); 2346 } 2347 2348 private void notifyValueChanged(int windowId, long nodeId) { 2349 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId); 2350 if (!isVirtualNode(virtualId)) { 2351 return; 2352 } 2353 final View view = findViewByAccessibilityId(windowId, nodeId); 2354 if (view == null) { 2355 return; 2356 } 2357 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId); 2358 if (node == null) { 2359 return; 2360 } 2361 AutofillManager.this.notifyValueChanged(view, virtualId, 2362 AutofillValue.forText(node.getText())); 2363 } 2364 2365 private void notifyViewClicked(int windowId, long nodeId) { 2366 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId); 2367 if (!isVirtualNode(virtualId)) { 2368 return; 2369 } 2370 final View view = findViewByAccessibilityId(windowId, nodeId); 2371 if (view == null) { 2372 return; 2373 } 2374 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId); 2375 if (node == null) { 2376 return; 2377 } 2378 AutofillManager.this.notifyViewClicked(view, virtualId); 2379 } 2380 2381 @GuardedBy("mLock") 2382 private void updateTrackedViewsLocked() { 2383 if (mTrackedViews != null) { 2384 mTrackedViews.onVisibleForAutofillChangedLocked(); 2385 } 2386 } 2387 2388 private View findViewByAccessibilityId(int windowId, long nodeId) { 2389 final AutofillClient client = getClient(); 2390 if (client == null) { 2391 return null; 2392 } 2393 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId); 2394 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId); 2395 } 2396 2397 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) { 2398 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); 2399 if (provider == null) { 2400 return null; 2401 } 2402 return provider.createAccessibilityNodeInfo(virtualId); 2403 } 2404 2405 private boolean isVirtualNode(int nodeId) { 2406 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID 2407 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 2408 } 2409 } 2410 2411 /** 2412 * View tracking information. Once all tracked views become invisible the session is finished. 2413 */ 2414 private class TrackedViews { 2415 /** Visible tracked views */ 2416 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; 2417 2418 /** Invisible tracked views */ 2419 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; 2420 2421 /** 2422 * Check if set is null or value is in set. 2423 * 2424 * @param set The set or null (== empty set) 2425 * @param value The value that might be in the set 2426 * 2427 * @return {@code true} iff set is not empty and value is in set 2428 */ 2429 // TODO: move to Helper as static method 2430 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) { 2431 return set != null && set.contains(value); 2432 } 2433 2434 /** 2435 * Add a value to a set. If set is null, create a new set. 2436 * 2437 * @param set The set or null (== empty set) 2438 * @param valueToAdd The value to add 2439 * 2440 * @return The set including the new value. If set was {@code null}, a set containing only 2441 * the new value. 2442 */ 2443 // TODO: move to Helper as static method 2444 @NonNull 2445 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) { 2446 if (set == null) { 2447 set = new ArraySet<>(1); 2448 } 2449 2450 set.add(valueToAdd); 2451 2452 return set; 2453 } 2454 2455 /** 2456 * Remove a value from a set. 2457 * 2458 * @param set The set or null (== empty set) 2459 * @param valueToRemove The value to remove 2460 * 2461 * @return The set without the removed value. {@code null} if set was null, or is empty 2462 * after removal. 2463 */ 2464 // TODO: move to Helper as static method 2465 @Nullable 2466 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) { 2467 if (set == null) { 2468 return null; 2469 } 2470 2471 set.remove(valueToRemove); 2472 2473 if (set.isEmpty()) { 2474 return null; 2475 } 2476 2477 return set; 2478 } 2479 2480 /** 2481 * Set the tracked views. 2482 * 2483 * @param trackedIds The views to be tracked 2484 */ 2485 TrackedViews(@Nullable AutofillId[] trackedIds) { 2486 final AutofillClient client = getClient(); 2487 if (!ArrayUtils.isEmpty(trackedIds) && client != null) { 2488 final boolean[] isVisible; 2489 2490 if (client.autofillClientIsVisibleForAutofill()) { 2491 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); 2492 isVisible = client.autofillClientGetViewVisibility(trackedIds); 2493 } else { 2494 // All false 2495 isVisible = new boolean[trackedIds.length]; 2496 } 2497 2498 final int numIds = trackedIds.length; 2499 for (int i = 0; i < numIds; i++) { 2500 final AutofillId id = trackedIds[i]; 2501 2502 if (isVisible[i]) { 2503 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 2504 } else { 2505 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 2506 } 2507 } 2508 } 2509 2510 if (sVerbose) { 2511 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " 2512 + " mVisibleTrackedIds=" + mVisibleTrackedIds 2513 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); 2514 } 2515 2516 if (mVisibleTrackedIds == null) { 2517 finishSessionLocked(); 2518 } 2519 } 2520 2521 /** 2522 * Called when a {@link View view's} visibility changes. 2523 * 2524 * @param id the id of the view/virtual view whose visibility changed. 2525 * @param isVisible visible if the view is visible in the view hierarchy. 2526 */ 2527 @GuardedBy("mLock") 2528 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) { 2529 if (sDebug) { 2530 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible=" 2531 + isVisible); 2532 } 2533 2534 if (isClientVisibleForAutofillLocked()) { 2535 if (isVisible) { 2536 if (isInSet(mInvisibleTrackedIds, id)) { 2537 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); 2538 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 2539 } 2540 } else { 2541 if (isInSet(mVisibleTrackedIds, id)) { 2542 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); 2543 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 2544 } 2545 } 2546 } 2547 2548 if (mVisibleTrackedIds == null) { 2549 if (sVerbose) { 2550 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds); 2551 } 2552 finishSessionLocked(); 2553 } 2554 } 2555 2556 /** 2557 * Called once the client becomes visible. 2558 * 2559 * @see AutofillClient#autofillClientIsVisibleForAutofill() 2560 */ 2561 @GuardedBy("mLock") 2562 void onVisibleForAutofillChangedLocked() { 2563 // The visibility of the views might have changed while the client was not be visible, 2564 // hence update the visibility state for all views. 2565 AutofillClient client = getClient(); 2566 ArraySet<AutofillId> updatedVisibleTrackedIds = null; 2567 ArraySet<AutofillId> updatedInvisibleTrackedIds = null; 2568 if (client != null) { 2569 if (sVerbose) { 2570 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds 2571 + " vis=" + mVisibleTrackedIds); 2572 } 2573 if (mInvisibleTrackedIds != null) { 2574 final ArrayList<AutofillId> orderedInvisibleIds = 2575 new ArrayList<>(mInvisibleTrackedIds); 2576 final boolean[] isVisible = client.autofillClientGetViewVisibility( 2577 Helper.toArray(orderedInvisibleIds)); 2578 2579 final int numInvisibleTrackedIds = orderedInvisibleIds.size(); 2580 for (int i = 0; i < numInvisibleTrackedIds; i++) { 2581 final AutofillId id = orderedInvisibleIds.get(i); 2582 if (isVisible[i]) { 2583 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 2584 2585 if (sDebug) { 2586 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible"); 2587 } 2588 } else { 2589 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 2590 } 2591 } 2592 } 2593 2594 if (mVisibleTrackedIds != null) { 2595 final ArrayList<AutofillId> orderedVisibleIds = 2596 new ArrayList<>(mVisibleTrackedIds); 2597 final boolean[] isVisible = client.autofillClientGetViewVisibility( 2598 Helper.toArray(orderedVisibleIds)); 2599 2600 final int numVisibleTrackedIds = orderedVisibleIds.size(); 2601 for (int i = 0; i < numVisibleTrackedIds; i++) { 2602 final AutofillId id = orderedVisibleIds.get(i); 2603 2604 if (isVisible[i]) { 2605 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 2606 } else { 2607 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 2608 2609 if (sDebug) { 2610 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible"); 2611 } 2612 } 2613 } 2614 } 2615 2616 mInvisibleTrackedIds = updatedInvisibleTrackedIds; 2617 mVisibleTrackedIds = updatedVisibleTrackedIds; 2618 } 2619 2620 if (mVisibleTrackedIds == null) { 2621 if (sVerbose) { 2622 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); 2623 } 2624 finishSessionLocked(); 2625 } 2626 } 2627 } 2628 2629 /** 2630 * Callback for autofill related events. 2631 * 2632 * <p>Typically used for applications that display their own "auto-complete" views, so they can 2633 * enable / disable such views when the autofill UI is shown / hidden. 2634 */ 2635 public abstract static class AutofillCallback { 2636 2637 /** @hide */ 2638 @IntDef(prefix = { "EVENT_INPUT_" }, value = { 2639 EVENT_INPUT_SHOWN, 2640 EVENT_INPUT_HIDDEN, 2641 EVENT_INPUT_UNAVAILABLE 2642 }) 2643 @Retention(RetentionPolicy.SOURCE) 2644 public @interface AutofillEventType {} 2645 2646 /** 2647 * The autofill input UI associated with the view was shown. 2648 * 2649 * <p>If the view provides its own auto-complete UI and its currently shown, it 2650 * should be hidden upon receiving this event. 2651 */ 2652 public static final int EVENT_INPUT_SHOWN = 1; 2653 2654 /** 2655 * The autofill input UI associated with the view was hidden. 2656 * 2657 * <p>If the view provides its own auto-complete UI that was hidden upon a 2658 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now. 2659 */ 2660 public static final int EVENT_INPUT_HIDDEN = 2; 2661 2662 /** 2663 * The autofill input UI associated with the view isn't shown because 2664 * autofill is not available. 2665 * 2666 * <p>If the view provides its own auto-complete UI but was not displaying it 2667 * to avoid flickering, it could shown it upon receiving this event. 2668 */ 2669 public static final int EVENT_INPUT_UNAVAILABLE = 3; 2670 2671 /** 2672 * Called after a change in the autofill state associated with a view. 2673 * 2674 * @param view view associated with the change. 2675 * 2676 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 2677 */ 2678 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) { 2679 } 2680 2681 /** 2682 * Called after a change in the autofill state associated with a virtual view. 2683 * 2684 * @param view parent view associated with the change. 2685 * @param virtualId id identifying the virtual child inside the parent view. 2686 * 2687 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 2688 */ 2689 public void onAutofillEvent(@NonNull View view, int virtualId, 2690 @AutofillEventType int event) { 2691 } 2692 } 2693 2694 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub { 2695 private final WeakReference<AutofillManager> mAfm; 2696 2697 AutofillManagerClient(AutofillManager autofillManager) { 2698 mAfm = new WeakReference<>(autofillManager); 2699 } 2700 2701 @Override 2702 public void setState(int flags) { 2703 final AutofillManager afm = mAfm.get(); 2704 if (afm != null) { 2705 afm.post(() -> afm.setState(flags)); 2706 } 2707 } 2708 2709 @Override 2710 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 2711 final AutofillManager afm = mAfm.get(); 2712 if (afm != null) { 2713 afm.post(() -> afm.autofill(sessionId, ids, values)); 2714 } 2715 } 2716 2717 @Override 2718 public void authenticate(int sessionId, int authenticationId, IntentSender intent, 2719 Intent fillInIntent) { 2720 final AutofillManager afm = mAfm.get(); 2721 if (afm != null) { 2722 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent)); 2723 } 2724 } 2725 2726 @Override 2727 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 2728 Rect anchorBounds, IAutofillWindowPresenter presenter) { 2729 final AutofillManager afm = mAfm.get(); 2730 if (afm != null) { 2731 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds, 2732 presenter)); 2733 } 2734 } 2735 2736 @Override 2737 public void requestHideFillUi(int sessionId, AutofillId id) { 2738 final AutofillManager afm = mAfm.get(); 2739 if (afm != null) { 2740 afm.post(() -> afm.requestHideFillUi(id, false)); 2741 } 2742 } 2743 2744 @Override 2745 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) { 2746 final AutofillManager afm = mAfm.get(); 2747 if (afm != null) { 2748 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState)); 2749 } 2750 } 2751 2752 @Override 2753 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) { 2754 final AutofillManager afm = mAfm.get(); 2755 if (afm != null) { 2756 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen)); 2757 } 2758 } 2759 2760 @Override 2761 public void startIntentSender(IntentSender intentSender, Intent intent) { 2762 final AutofillManager afm = mAfm.get(); 2763 if (afm != null) { 2764 afm.post(() -> { 2765 try { 2766 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0); 2767 } catch (IntentSender.SendIntentException e) { 2768 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e); 2769 } 2770 }); 2771 } 2772 } 2773 2774 @Override 2775 public void setTrackedViews(int sessionId, AutofillId[] ids, 2776 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds, 2777 AutofillId saveTriggerId) { 2778 final AutofillManager afm = mAfm.get(); 2779 if (afm != null) { 2780 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, 2781 saveOnFinish, fillableIds, saveTriggerId)); 2782 } 2783 } 2784 2785 @Override 2786 public void setSaveUiState(int sessionId, boolean shown) { 2787 final AutofillManager afm = mAfm.get(); 2788 if (afm != null) { 2789 afm.post(() -> afm.setSaveUiState(sessionId, shown)); 2790 } 2791 } 2792 2793 @Override 2794 public void setSessionFinished(int newState) { 2795 final AutofillManager afm = mAfm.get(); 2796 if (afm != null) { 2797 afm.post(() -> afm.setSessionFinished(newState)); 2798 } 2799 } 2800 } 2801} 2802