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