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