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