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