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