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