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