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