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.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SystemService; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentSender; 30 import android.graphics.Rect; 31 import android.metrics.LogMaker; 32 import android.os.Bundle; 33 import android.os.Parcelable; 34 import android.os.RemoteException; 35 import android.service.autofill.AutofillService; 36 import android.service.autofill.FillEventHistory; 37 import android.util.ArrayMap; 38 import android.util.ArraySet; 39 import android.util.Log; 40 import android.util.SparseArray; 41 import android.view.View; 42 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.logging.MetricsLogger; 45 import com.android.internal.logging.nano.MetricsProto; 46 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.lang.ref.WeakReference; 50 import java.util.ArrayList; 51 import java.util.List; 52 import java.util.Objects; 53 54 /** 55 * App entry point to the Autofill Framework. 56 * 57 * <p>It is safe to call into this from any thread. 58 */ 59 @SystemService(Context.AUTOFILL_MANAGER_SERVICE) 60 public final class AutofillManager { 61 62 private static final String TAG = "AutofillManager"; 63 64 /** 65 * Intent extra: The assist structure which captures the filled screen. 66 * 67 * <p> 68 * Type: {@link android.app.assist.AssistStructure} 69 */ 70 public static final String EXTRA_ASSIST_STRUCTURE = 71 "android.view.autofill.extra.ASSIST_STRUCTURE"; 72 73 /** 74 * Intent extra: The result of an authentication operation. It is 75 * either a fully populated {@link android.service.autofill.FillResponse} 76 * or a fully populated {@link android.service.autofill.Dataset} if 77 * a response or a dataset is being authenticated respectively. 78 * 79 * <p> 80 * Type: {@link android.service.autofill.FillResponse} or a 81 * {@link android.service.autofill.Dataset} 82 */ 83 public static final String EXTRA_AUTHENTICATION_RESULT = 84 "android.view.autofill.extra.AUTHENTICATION_RESULT"; 85 86 /** 87 * Intent extra: The optional extras provided by the 88 * {@link android.service.autofill.AutofillService}. 89 * 90 * <p>For example, when the service responds to a {@link 91 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with 92 * a {@code FillResponse} that requires authentication, the Intent that launches the 93 * service authentication will contain the Bundle set by 94 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra. 95 * 96 * <p> 97 * Type: {@link android.os.Bundle} 98 */ 99 public static final String EXTRA_CLIENT_STATE = 100 "android.view.autofill.extra.CLIENT_STATE"; 101 102 static final String SESSION_ID_TAG = "android:sessionId"; 103 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData"; 104 105 /** @hide */ public static final int ACTION_START_SESSION = 1; 106 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2; 107 /** @hide */ public static final int ACTION_VIEW_EXITED = 3; 108 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4; 109 110 111 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1; 112 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; 113 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; 114 115 /** Which bits in an authentication id are used for the dataset id */ 116 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF; 117 /** How many bits in an authentication id are used for the dataset id */ 118 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16; 119 /** @hide The index for an undefined data set */ 120 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF; 121 122 /** 123 * Makes an authentication id from a request id and a dataset id. 124 * 125 * @param requestId The request id. 126 * @param datasetId The dataset id. 127 * @return The authentication id. 128 * @hide 129 */ makeAuthenticationId(int requestId, int datasetId)130 public static int makeAuthenticationId(int requestId, int datasetId) { 131 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT) 132 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK); 133 } 134 135 /** 136 * Gets the request id from an authentication id. 137 * 138 * @param authRequestId The authentication id. 139 * @return The request id. 140 * @hide 141 */ getRequestIdFromAuthenticationId(int authRequestId)142 public static int getRequestIdFromAuthenticationId(int authRequestId) { 143 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT); 144 } 145 146 /** 147 * Gets the dataset id from an authentication id. 148 * 149 * @param authRequestId The authentication id. 150 * @return The dataset id. 151 * @hide 152 */ getDatasetIdFromAuthenticationId(int authRequestId)153 public static int getDatasetIdFromAuthenticationId(int authRequestId) { 154 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK); 155 } 156 157 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 158 159 /** 160 * There is currently no session running. 161 * {@hide} 162 */ 163 public static final int NO_SESSION = Integer.MIN_VALUE; 164 165 private final IAutoFillManager mService; 166 167 private final Object mLock = new Object(); 168 169 @GuardedBy("mLock") 170 private IAutoFillManagerClient mServiceClient; 171 172 @GuardedBy("mLock") 173 private AutofillCallback mCallback; 174 175 private final Context mContext; 176 177 @GuardedBy("mLock") 178 private int mSessionId = NO_SESSION; 179 180 @GuardedBy("mLock") 181 private boolean mEnabled; 182 183 /** If a view changes to this mapping the autofill operation was successful */ 184 @GuardedBy("mLock") 185 @Nullable private ParcelableMap mLastAutofilledData; 186 187 /** If view tracking is enabled, contains the tracking state */ 188 @GuardedBy("mLock") 189 @Nullable private TrackedViews mTrackedViews; 190 191 /** Views that are only tracked because they are fillable and could be anchoring the UI. */ 192 @GuardedBy("mLock") 193 @Nullable private ArraySet<AutofillId> mFillableIds; 194 195 /** @hide */ 196 public interface AutofillClient { 197 /** 198 * Asks the client to start an authentication flow. 199 * 200 * @param authenticationId A unique id of the authentication operation. 201 * @param intent The authentication intent. 202 * @param fillInIntent The authentication fill-in intent. 203 */ autofillCallbackAuthenticate(int authenticationId, IntentSender intent, Intent fillInIntent)204 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent, 205 Intent fillInIntent); 206 207 /** 208 * Tells the client this manager has state to be reset. 209 */ autofillCallbackResetableStateAvailable()210 void autofillCallbackResetableStateAvailable(); 211 212 /** 213 * Request showing the autofill UI. 214 * 215 * @param anchor The real view the UI needs to anchor to. 216 * @param width The width of the fill UI content. 217 * @param height The height of the fill UI content. 218 * @param virtualBounds The bounds of the virtual decendant of the anchor. 219 * @param presenter The presenter that controls the fill UI window. 220 * @return Whether the UI was shown. 221 */ autofillCallbackRequestShowFillUi(@onNull View anchor, int width, int height, @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter)222 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, 223 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); 224 225 /** 226 * Request hiding the autofill UI. 227 * 228 * @return Whether the UI was hidden. 229 */ autofillCallbackRequestHideFillUi()230 boolean autofillCallbackRequestHideFillUi(); 231 232 /** 233 * Checks if views are currently attached and visible. 234 * 235 * @return And array with {@code true} iff the view is attached or visible 236 */ getViewVisibility(@onNull int[] viewId)237 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId); 238 239 /** 240 * Checks is the client is currently visible as understood by autofill. 241 * 242 * @return {@code true} if the client is currently visible 243 */ isVisibleForAutofill()244 boolean isVisibleForAutofill(); 245 246 /** 247 * Finds views by traversing the hierarchies of the client. 248 * 249 * @param viewIds The autofill ids of the views to find 250 * 251 * @return And array containing the views (empty if no views found). 252 */ findViewsByAutofillIdTraversal(@onNull int[] viewIds)253 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds); 254 255 /** 256 * Finds a view by traversing the hierarchies of the client. 257 * 258 * @param viewId The autofill id of the views to find 259 * 260 * @return The view, or {@code null} if not found 261 */ findViewByAutofillIdTraversal(int viewId)262 @Nullable View findViewByAutofillIdTraversal(int viewId); 263 264 /** 265 * Runs the specified action on the UI thread. 266 */ runOnUiThread(Runnable action)267 void runOnUiThread(Runnable action); 268 } 269 270 /** 271 * @hide 272 */ AutofillManager(Context context, IAutoFillManager service)273 public AutofillManager(Context context, IAutoFillManager service) { 274 mContext = context; 275 mService = service; 276 } 277 278 /** 279 * Restore state after activity lifecycle 280 * 281 * @param savedInstanceState The state to be restored 282 * 283 * {@hide} 284 */ onCreate(Bundle savedInstanceState)285 public void onCreate(Bundle savedInstanceState) { 286 if (!hasAutofillFeature()) { 287 return; 288 } 289 synchronized (mLock) { 290 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG); 291 292 if (mSessionId != NO_SESSION) { 293 Log.w(TAG, "New session was started before onCreate()"); 294 return; 295 } 296 297 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION); 298 299 if (mSessionId != NO_SESSION) { 300 ensureServiceClientAddedIfNeededLocked(); 301 302 final AutofillClient client = getClientLocked(); 303 if (client != null) { 304 try { 305 final boolean sessionWasRestored = mService.restoreSession(mSessionId, 306 mContext.getActivityToken(), mServiceClient.asBinder()); 307 308 if (!sessionWasRestored) { 309 Log.w(TAG, "Session " + mSessionId + " could not be restored"); 310 mSessionId = NO_SESSION; 311 } else { 312 if (sDebug) { 313 Log.d(TAG, "session " + mSessionId + " was restored"); 314 } 315 316 client.autofillCallbackResetableStateAvailable(); 317 } 318 } catch (RemoteException e) { 319 Log.e(TAG, "Could not figure out if there was an autofill session", e); 320 } 321 } 322 } 323 } 324 } 325 326 /** 327 * Called once the client becomes visible. 328 * 329 * @see AutofillClient#isVisibleForAutofill() 330 * 331 * {@hide} 332 */ onVisibleForAutofill()333 public void onVisibleForAutofill() { 334 synchronized (mLock) { 335 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) { 336 mTrackedViews.onVisibleForAutofillLocked(); 337 } 338 } 339 } 340 341 /** 342 * Save state before activity lifecycle 343 * 344 * @param outState Place to store the state 345 * 346 * {@hide} 347 */ onSaveInstanceState(Bundle outState)348 public void onSaveInstanceState(Bundle outState) { 349 if (!hasAutofillFeature()) { 350 return; 351 } 352 synchronized (mLock) { 353 if (mSessionId != NO_SESSION) { 354 outState.putInt(SESSION_ID_TAG, mSessionId); 355 } 356 357 if (mLastAutofilledData != null) { 358 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData); 359 } 360 } 361 } 362 363 /** 364 * Checks whether autofill is enabled for the current user. 365 * 366 * <p>Typically used to determine whether the option to explicitly request autofill should 367 * be offered - see {@link #requestAutofill(View)}. 368 * 369 * @return whether autofill is enabled for the current user. 370 */ isEnabled()371 public boolean isEnabled() { 372 if (!hasAutofillFeature()) { 373 return false; 374 } 375 synchronized (mLock) { 376 ensureServiceClientAddedIfNeededLocked(); 377 return mEnabled; 378 } 379 } 380 381 /** 382 * Should always be called from {@link AutofillService#getFillEventHistory()}. 383 * 384 * @hide 385 */ getFillEventHistory()386 @Nullable public FillEventHistory getFillEventHistory() { 387 try { 388 return mService.getFillEventHistory(); 389 } catch (RemoteException e) { 390 e.rethrowFromSystemServer(); 391 return null; 392 } 393 } 394 395 /** 396 * Explicitly requests a new autofill context. 397 * 398 * <p>Normally, the autofill context is automatically started if necessary when 399 * {@link #notifyViewEntered(View)} is called, but this method should be used in the 400 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL 401 * option on its contextual overflow menu, and the user selects it. 402 * 403 * @param view view requesting the new autofill context. 404 */ requestAutofill(@onNull View view)405 public void requestAutofill(@NonNull View view) { 406 notifyViewEntered(view, FLAG_MANUAL_REQUEST); 407 } 408 409 /** 410 * Explicitly requests a new autofill context for virtual views. 411 * 412 * <p>Normally, the autofill context is automatically started if necessary when 413 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the 414 * cases where it must be explicitly started. For example, when the virtual view offers an 415 * AUTOFILL option on its contextual overflow menu, and the user selects it. 416 * 417 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 418 * parent view uses {@code bounds} to draw the virtual view inside its Canvas, 419 * the absolute bounds could be calculated by: 420 * 421 * <pre class="prettyprint"> 422 * int offset[] = new int[2]; 423 * getLocationOnScreen(offset); 424 * Rect absBounds = new Rect(bounds.left + offset[0], 425 * bounds.top + offset[1], 426 * bounds.right + offset[0], bounds.bottom + offset[1]); 427 * </pre> 428 * 429 * @param view the virtual view parent. 430 * @param virtualId id identifying the virtual child inside the parent view. 431 * @param absBounds absolute boundaries of the virtual view in the screen. 432 */ requestAutofill(@onNull View view, int virtualId, @NonNull Rect absBounds)433 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 434 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST); 435 } 436 437 /** 438 * Called when a {@link View} that supports autofill is entered. 439 * 440 * @param view {@link View} that was entered. 441 */ notifyViewEntered(@onNull View view)442 public void notifyViewEntered(@NonNull View view) { 443 notifyViewEntered(view, 0); 444 } 445 notifyViewEntered(@onNull View view, int flags)446 private void notifyViewEntered(@NonNull View view, int flags) { 447 if (!hasAutofillFeature()) { 448 return; 449 } 450 AutofillCallback callback = null; 451 synchronized (mLock) { 452 ensureServiceClientAddedIfNeededLocked(); 453 454 if (!mEnabled) { 455 if (mCallback != null) { 456 callback = mCallback; 457 } 458 } else { 459 final AutofillId id = getAutofillId(view); 460 final AutofillValue value = view.getAutofillValue(); 461 462 if (mSessionId == NO_SESSION) { 463 // Starts new session. 464 startSessionLocked(id, null, value, flags); 465 } else { 466 // Update focus on existing session. 467 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); 468 } 469 } 470 } 471 472 if (callback != null) { 473 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 474 } 475 } 476 477 /** 478 * Called when a {@link View} that supports autofill is exited. 479 * 480 * @param view {@link View} that was exited. 481 */ notifyViewExited(@onNull View view)482 public void notifyViewExited(@NonNull View view) { 483 if (!hasAutofillFeature()) { 484 return; 485 } 486 synchronized (mLock) { 487 ensureServiceClientAddedIfNeededLocked(); 488 489 if (mEnabled && mSessionId != NO_SESSION) { 490 final AutofillId id = getAutofillId(view); 491 492 // Update focus on existing session. 493 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 494 } 495 } 496 } 497 498 /** 499 * Called when a {@link View view's} visibility changes. 500 * 501 * @param view {@link View} that was exited. 502 * @param isVisible visible if the view is visible in the view hierarchy. 503 * 504 * @hide 505 */ notifyViewVisibilityChange(@onNull View view, boolean isVisible)506 public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { 507 synchronized (mLock) { 508 if (mEnabled && mSessionId != NO_SESSION) { 509 if (!isVisible && mFillableIds != null) { 510 final AutofillId id = view.getAutofillId(); 511 if (mFillableIds.contains(id)) { 512 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible"); 513 requestHideFillUi(id, view); 514 } 515 } 516 if (mTrackedViews != null) { 517 mTrackedViews.notifyViewVisibilityChange(view, isVisible); 518 } 519 } 520 } 521 } 522 523 /** 524 * Called when a virtual view that supports autofill is entered. 525 * 526 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the 527 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas, 528 * the absolute bounds could be calculated by: 529 * 530 * <pre class="prettyprint"> 531 * int offset[] = new int[2]; 532 * getLocationOnScreen(offset); 533 * Rect absBounds = new Rect(bounds.left + offset[0], 534 * bounds.top + offset[1], 535 * bounds.right + offset[0], bounds.bottom + offset[1]); 536 * </pre> 537 * 538 * @param view the virtual view parent. 539 * @param virtualId id identifying the virtual child inside the parent view. 540 * @param absBounds absolute boundaries of the virtual view in the screen. 541 */ notifyViewEntered(@onNull View view, int virtualId, @NonNull Rect absBounds)542 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) { 543 notifyViewEntered(view, virtualId, absBounds, 0); 544 } 545 notifyViewEntered(View view, int virtualId, Rect bounds, int flags)546 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) { 547 if (!hasAutofillFeature()) { 548 return; 549 } 550 AutofillCallback callback = null; 551 synchronized (mLock) { 552 ensureServiceClientAddedIfNeededLocked(); 553 554 if (!mEnabled) { 555 if (mCallback != null) { 556 callback = mCallback; 557 } 558 } else { 559 final AutofillId id = getAutofillId(view, virtualId); 560 561 if (mSessionId == NO_SESSION) { 562 // Starts new session. 563 startSessionLocked(id, bounds, null, flags); 564 } else { 565 // Update focus on existing session. 566 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); 567 } 568 } 569 } 570 571 if (callback != null) { 572 callback.onAutofillEvent(view, virtualId, 573 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 574 } 575 } 576 577 /** 578 * Called when a virtual view that supports autofill is exited. 579 * 580 * @param view the virtual view parent. 581 * @param virtualId id identifying the virtual child inside the parent view. 582 */ notifyViewExited(@onNull View view, int virtualId)583 public void notifyViewExited(@NonNull View view, int virtualId) { 584 if (!hasAutofillFeature()) { 585 return; 586 } 587 synchronized (mLock) { 588 ensureServiceClientAddedIfNeededLocked(); 589 590 if (mEnabled && mSessionId != NO_SESSION) { 591 final AutofillId id = getAutofillId(view, virtualId); 592 593 // Update focus on existing session. 594 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); 595 } 596 } 597 } 598 599 /** 600 * Called to indicate the value of an autofillable {@link View} changed. 601 * 602 * @param view view whose value changed. 603 */ notifyValueChanged(View view)604 public void notifyValueChanged(View view) { 605 if (!hasAutofillFeature()) { 606 return; 607 } 608 AutofillId id = null; 609 boolean valueWasRead = false; 610 AutofillValue value = null; 611 612 synchronized (mLock) { 613 // If the session is gone some fields might still be highlighted, hence we have to 614 // remove the isAutofilled property even if no sessions are active. 615 if (mLastAutofilledData == null) { 616 view.setAutofilled(false); 617 } else { 618 id = getAutofillId(view); 619 if (mLastAutofilledData.containsKey(id)) { 620 value = view.getAutofillValue(); 621 valueWasRead = true; 622 623 if (Objects.equals(mLastAutofilledData.get(id), value)) { 624 view.setAutofilled(true); 625 } else { 626 view.setAutofilled(false); 627 mLastAutofilledData.remove(id); 628 } 629 } else { 630 view.setAutofilled(false); 631 } 632 } 633 634 if (!mEnabled || mSessionId == NO_SESSION) { 635 return; 636 } 637 638 if (id == null) { 639 id = getAutofillId(view); 640 } 641 642 if (!valueWasRead) { 643 value = view.getAutofillValue(); 644 } 645 646 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 647 } 648 } 649 650 /** 651 * Called to indicate the value of an autofillable virtual view has changed. 652 * 653 * @param view the virtual view parent. 654 * @param virtualId id identifying the virtual child inside the parent view. 655 * @param value new value of the child. 656 */ notifyValueChanged(View view, int virtualId, AutofillValue value)657 public void notifyValueChanged(View view, int virtualId, AutofillValue value) { 658 if (!hasAutofillFeature()) { 659 return; 660 } 661 synchronized (mLock) { 662 if (!mEnabled || mSessionId == NO_SESSION) { 663 return; 664 } 665 666 final AutofillId id = getAutofillId(view, virtualId); 667 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0); 668 } 669 } 670 671 /** 672 * Called to indicate the current autofill context should be commited. 673 * 674 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should 675 * call this method after the form is submitted and another page is rendered. 676 */ commit()677 public void commit() { 678 if (!hasAutofillFeature()) { 679 return; 680 } 681 synchronized (mLock) { 682 if (!mEnabled && mSessionId == NO_SESSION) { 683 return; 684 } 685 686 finishSessionLocked(); 687 } 688 } 689 690 /** 691 * Called to indicate the current autofill context should be cancelled. 692 * 693 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should 694 * call this method if the user does not post the form but moves to another form in this page. 695 */ cancel()696 public void cancel() { 697 if (!hasAutofillFeature()) { 698 return; 699 } 700 synchronized (mLock) { 701 if (!mEnabled && mSessionId == NO_SESSION) { 702 return; 703 } 704 705 cancelSessionLocked(); 706 } 707 } 708 709 /** @hide */ disableOwnedAutofillServices()710 public void disableOwnedAutofillServices() { 711 disableAutofillServices(); 712 } 713 714 /** 715 * If the app calling this API has enabled autofill services they 716 * will be disabled. 717 */ disableAutofillServices()718 public void disableAutofillServices() { 719 if (!hasAutofillFeature()) { 720 return; 721 } 722 try { 723 mService.disableOwnedAutofillServices(mContext.getUserId()); 724 } catch (RemoteException e) { 725 throw e.rethrowFromSystemServer(); 726 } 727 } 728 729 /** 730 * Returns {@code true} if the calling application provides a {@link AutofillService} that is 731 * enabled for the current user, or {@code false} otherwise. 732 */ hasEnabledAutofillServices()733 public boolean hasEnabledAutofillServices() { 734 if (mService == null) return false; 735 736 try { 737 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName()); 738 } catch (RemoteException e) { 739 throw e.rethrowFromSystemServer(); 740 } 741 } 742 743 /** 744 * Returns {@code true} if autofill is supported by the current device and 745 * is supported for this user. 746 * 747 * <p>Autofill is typically supported, but it could be unsupported in cases like: 748 * <ol> 749 * <li>Low-end devices. 750 * <li>Device policy rules that forbid its usage. 751 * </ol> 752 */ isAutofillSupported()753 public boolean isAutofillSupported() { 754 if (mService == null) return false; 755 756 try { 757 return mService.isServiceSupported(mContext.getUserId()); 758 } catch (RemoteException e) { 759 throw e.rethrowFromSystemServer(); 760 } 761 } 762 getClientLocked()763 private AutofillClient getClientLocked() { 764 if (mContext instanceof AutofillClient) { 765 return (AutofillClient) mContext; 766 } 767 return null; 768 } 769 770 /** @hide */ onAuthenticationResult(int authenticationId, Intent data)771 public void onAuthenticationResult(int authenticationId, Intent data) { 772 if (!hasAutofillFeature()) { 773 return; 774 } 775 // TODO: the result code is being ignored, so this method is not reliably 776 // handling the cases where it's not RESULT_OK: it works fine if the service does not 777 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the 778 // service set the extra and returned RESULT_CANCELED... 779 780 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data); 781 782 synchronized (mLock) { 783 if (mSessionId == NO_SESSION || data == null) { 784 return; 785 } 786 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT); 787 final Bundle responseData = new Bundle(); 788 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); 789 try { 790 mService.setAuthenticationResult(responseData, mSessionId, authenticationId, 791 mContext.getUserId()); 792 } catch (RemoteException e) { 793 Log.e(TAG, "Error delivering authentication result", e); 794 } 795 } 796 } 797 getAutofillId(View view)798 private static AutofillId getAutofillId(View view) { 799 return new AutofillId(view.getAutofillViewId()); 800 } 801 getAutofillId(View parent, int virtualId)802 private static AutofillId getAutofillId(View parent, int virtualId) { 803 return new AutofillId(parent.getAutofillViewId(), virtualId); 804 } 805 startSessionLocked(@onNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags)806 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, 807 @NonNull AutofillValue value, int flags) { 808 if (sVerbose) { 809 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value 810 + ", flags=" + flags); 811 } 812 813 try { 814 mSessionId = mService.startSession(mContext.getActivityToken(), 815 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 816 mCallback != null, flags, mContext.getOpPackageName()); 817 final AutofillClient client = getClientLocked(); 818 if (client != null) { 819 client.autofillCallbackResetableStateAvailable(); 820 } 821 } catch (RemoteException e) { 822 throw e.rethrowFromSystemServer(); 823 } 824 } 825 finishSessionLocked()826 private void finishSessionLocked() { 827 if (sVerbose) Log.v(TAG, "finishSessionLocked()"); 828 829 try { 830 mService.finishSession(mSessionId, mContext.getUserId()); 831 } catch (RemoteException e) { 832 throw e.rethrowFromSystemServer(); 833 } 834 835 mTrackedViews = null; 836 mSessionId = NO_SESSION; 837 } 838 cancelSessionLocked()839 private void cancelSessionLocked() { 840 if (sVerbose) Log.v(TAG, "cancelSessionLocked()"); 841 842 try { 843 mService.cancelSession(mSessionId, mContext.getUserId()); 844 } catch (RemoteException e) { 845 throw e.rethrowFromSystemServer(); 846 } 847 848 resetSessionLocked(); 849 } 850 resetSessionLocked()851 private void resetSessionLocked() { 852 mSessionId = NO_SESSION; 853 mTrackedViews = null; 854 } 855 updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, int flags)856 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, 857 int flags) { 858 if (sVerbose && action != ACTION_VIEW_EXITED) { 859 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds 860 + ", value=" + value + ", action=" + action + ", flags=" + flags); 861 } 862 863 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0; 864 865 try { 866 if (restartIfNecessary) { 867 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(), 868 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), 869 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action); 870 if (newId != mSessionId) { 871 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); 872 mSessionId = newId; 873 final AutofillClient client = getClientLocked(); 874 if (client != null) { 875 client.autofillCallbackResetableStateAvailable(); 876 } 877 } 878 } else { 879 mService.updateSession(mSessionId, id, bounds, value, action, flags, 880 mContext.getUserId()); 881 } 882 883 } catch (RemoteException e) { 884 throw e.rethrowFromSystemServer(); 885 } 886 } 887 ensureServiceClientAddedIfNeededLocked()888 private void ensureServiceClientAddedIfNeededLocked() { 889 if (getClientLocked() == null) { 890 return; 891 } 892 893 if (mServiceClient == null) { 894 mServiceClient = new AutofillManagerClient(this); 895 try { 896 final int flags = mService.addClient(mServiceClient, mContext.getUserId()); 897 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; 898 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0; 899 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0; 900 } catch (RemoteException e) { 901 throw e.rethrowFromSystemServer(); 902 } 903 } 904 } 905 906 /** 907 * Registers a {@link AutofillCallback} to receive autofill events. 908 * 909 * @param callback callback to receive events. 910 */ registerCallback(@ullable AutofillCallback callback)911 public void registerCallback(@Nullable AutofillCallback callback) { 912 if (!hasAutofillFeature()) { 913 return; 914 } 915 synchronized (mLock) { 916 if (callback == null) return; 917 918 final boolean hadCallback = mCallback != null; 919 mCallback = callback; 920 921 if (!hadCallback) { 922 try { 923 mService.setHasCallback(mSessionId, mContext.getUserId(), true); 924 } catch (RemoteException e) { 925 throw e.rethrowFromSystemServer(); 926 } 927 } 928 } 929 } 930 931 /** 932 * Unregisters a {@link AutofillCallback} to receive autofill events. 933 * 934 * @param callback callback to stop receiving events. 935 */ unregisterCallback(@ullable AutofillCallback callback)936 public void unregisterCallback(@Nullable AutofillCallback callback) { 937 if (!hasAutofillFeature()) { 938 return; 939 } 940 synchronized (mLock) { 941 if (callback == null || mCallback == null || callback != mCallback) return; 942 943 mCallback = null; 944 945 try { 946 mService.setHasCallback(mSessionId, mContext.getUserId(), false); 947 } catch (RemoteException e) { 948 throw e.rethrowFromSystemServer(); 949 } 950 } 951 } 952 requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)953 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 954 Rect anchorBounds, IAutofillWindowPresenter presenter) { 955 final View anchor = findView(id); 956 if (anchor == null) { 957 return; 958 } 959 960 AutofillCallback callback = null; 961 synchronized (mLock) { 962 if (mSessionId == sessionId) { 963 AutofillClient client = getClientLocked(); 964 965 if (client != null) { 966 if (client.autofillCallbackRequestShowFillUi(anchor, width, height, 967 anchorBounds, presenter) && mCallback != null) { 968 callback = mCallback; 969 } 970 } 971 } 972 } 973 974 if (callback != null) { 975 if (id.isVirtual()) { 976 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 977 AutofillCallback.EVENT_INPUT_SHOWN); 978 } else { 979 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); 980 } 981 } 982 } 983 authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent)984 private void authenticate(int sessionId, int authenticationId, IntentSender intent, 985 Intent fillInIntent) { 986 synchronized (mLock) { 987 if (sessionId == mSessionId) { 988 AutofillClient client = getClientLocked(); 989 if (client != null) { 990 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent); 991 } 992 } 993 } 994 } 995 setState(boolean enabled, boolean resetSession, boolean resetClient)996 private void setState(boolean enabled, boolean resetSession, boolean resetClient) { 997 synchronized (mLock) { 998 mEnabled = enabled; 999 if (!mEnabled || resetSession) { 1000 // Reset the session state 1001 resetSessionLocked(); 1002 } 1003 if (resetClient) { 1004 // Reset connection to system 1005 mServiceClient = null; 1006 } 1007 } 1008 } 1009 1010 /** 1011 * Sets a view as autofilled if the current value is the {code targetValue}. 1012 * 1013 * @param view The view that is to be autofilled 1014 * @param targetValue The value we want to fill into view 1015 */ setAutofilledIfValuesIs(@onNull View view, @Nullable AutofillValue targetValue)1016 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) { 1017 AutofillValue currentValue = view.getAutofillValue(); 1018 if (Objects.equals(currentValue, targetValue)) { 1019 synchronized (mLock) { 1020 if (mLastAutofilledData == null) { 1021 mLastAutofilledData = new ParcelableMap(1); 1022 } 1023 mLastAutofilledData.put(getAutofillId(view), targetValue); 1024 } 1025 view.setAutofilled(true); 1026 } 1027 } 1028 autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values)1029 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 1030 synchronized (mLock) { 1031 if (sessionId != mSessionId) { 1032 return; 1033 } 1034 1035 final AutofillClient client = getClientLocked(); 1036 if (client == null) { 1037 return; 1038 } 1039 1040 final int itemCount = ids.size(); 1041 int numApplied = 0; 1042 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; 1043 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids)); 1044 1045 for (int i = 0; i < itemCount; i++) { 1046 final AutofillId id = ids.get(i); 1047 final AutofillValue value = values.get(i); 1048 final int viewId = id.getViewId(); 1049 final View view = views[i]; 1050 if (view == null) { 1051 Log.w(TAG, "autofill(): no View with id " + viewId); 1052 continue; 1053 } 1054 if (id.isVirtual()) { 1055 if (virtualValues == null) { 1056 // Most likely there will be just one view with virtual children. 1057 virtualValues = new ArrayMap<>(1); 1058 } 1059 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); 1060 if (valuesByParent == null) { 1061 // We don't know the size yet, but usually it will be just a few fields... 1062 valuesByParent = new SparseArray<>(5); 1063 virtualValues.put(view, valuesByParent); 1064 } 1065 valuesByParent.put(id.getVirtualChildId(), value); 1066 } else { 1067 // Mark the view as to be autofilled with 'value' 1068 if (mLastAutofilledData == null) { 1069 mLastAutofilledData = new ParcelableMap(itemCount - i); 1070 } 1071 mLastAutofilledData.put(id, value); 1072 1073 view.autofill(value); 1074 1075 // Set as autofilled if the values match now, e.g. when the value was updated 1076 // synchronously. 1077 // If autofill happens async, the view is set to autofilled in 1078 // notifyValueChanged. 1079 setAutofilledIfValuesIs(view, value); 1080 1081 numApplied++; 1082 } 1083 } 1084 1085 if (virtualValues != null) { 1086 for (int i = 0; i < virtualValues.size(); i++) { 1087 final View parent = virtualValues.keyAt(i); 1088 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); 1089 parent.autofill(childrenValues); 1090 numApplied += childrenValues.size(); 1091 } 1092 } 1093 1094 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); 1095 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); 1096 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, 1097 numApplied); 1098 mMetricsLogger.write(log); 1099 } 1100 } 1101 1102 /** 1103 * Set the tracked views. 1104 * 1105 * @param trackedIds The views to be tracked 1106 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible. 1107 * @param fillableIds Views that might anchor FillUI. 1108 */ setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds)1109 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, 1110 boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) { 1111 synchronized (mLock) { 1112 if (mEnabled && mSessionId == sessionId) { 1113 if (saveOnAllViewsInvisible) { 1114 mTrackedViews = new TrackedViews(trackedIds); 1115 } else { 1116 mTrackedViews = null; 1117 } 1118 if (fillableIds != null) { 1119 if (mFillableIds == null) { 1120 mFillableIds = new ArraySet<>(fillableIds.length); 1121 } 1122 for (AutofillId id : fillableIds) { 1123 mFillableIds.add(id); 1124 } 1125 if (sVerbose) { 1126 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds 1127 + ", mFillableIds" + mFillableIds); 1128 } 1129 } 1130 } 1131 } 1132 } 1133 requestHideFillUi(AutofillId id)1134 private void requestHideFillUi(AutofillId id) { 1135 final View anchor = findView(id); 1136 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor); 1137 if (anchor == null) { 1138 return; 1139 } 1140 requestHideFillUi(id, anchor); 1141 } 1142 requestHideFillUi(AutofillId id, View anchor)1143 private void requestHideFillUi(AutofillId id, View anchor) { 1144 1145 AutofillCallback callback = null; 1146 synchronized (mLock) { 1147 // We do not check the session id for two reasons: 1148 // 1. If local and remote session id are off sync the UI would be stuck shown 1149 // 2. There is a race between the user state being destroyed due the fill 1150 // service being uninstalled and the UI being dismissed. 1151 AutofillClient client = getClientLocked(); 1152 if (client != null) { 1153 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) { 1154 callback = mCallback; 1155 } 1156 } 1157 } 1158 1159 if (callback != null) { 1160 if (id.isVirtual()) { 1161 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1162 AutofillCallback.EVENT_INPUT_HIDDEN); 1163 } else { 1164 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); 1165 } 1166 } 1167 } 1168 notifyNoFillUi(int sessionId, AutofillId id)1169 private void notifyNoFillUi(int sessionId, AutofillId id) { 1170 final View anchor = findView(id); 1171 if (anchor == null) { 1172 return; 1173 } 1174 1175 AutofillCallback callback = null; 1176 synchronized (mLock) { 1177 if (mSessionId == sessionId && getClientLocked() != null) { 1178 callback = mCallback; 1179 } 1180 } 1181 1182 if (callback != null) { 1183 if (id.isVirtual()) { 1184 callback.onAutofillEvent(anchor, id.getVirtualChildId(), 1185 AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1186 } else { 1187 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1188 } 1189 1190 } 1191 } 1192 1193 /** 1194 * Get an array of viewIds from a List of {@link AutofillId}. 1195 * 1196 * @param autofillIds The autofill ids to convert 1197 * 1198 * @return The array of viewIds. 1199 */ 1200 // TODO: move to Helper as static method getViewIds(@onNull AutofillId[] autofillIds)1201 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) { 1202 final int numIds = autofillIds.length; 1203 final int[] viewIds = new int[numIds]; 1204 for (int i = 0; i < numIds; i++) { 1205 viewIds[i] = autofillIds[i].getViewId(); 1206 } 1207 1208 return viewIds; 1209 } 1210 1211 // TODO: move to Helper as static method getViewIds(@onNull List<AutofillId> autofillIds)1212 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) { 1213 final int numIds = autofillIds.size(); 1214 final int[] viewIds = new int[numIds]; 1215 for (int i = 0; i < numIds; i++) { 1216 viewIds[i] = autofillIds.get(i).getViewId(); 1217 } 1218 1219 return viewIds; 1220 } 1221 1222 /** 1223 * Find a single view by its id. 1224 * 1225 * @param autofillId The autofill id of the view 1226 * 1227 * @return The view or {@code null} if view was not found 1228 */ findView(@onNull AutofillId autofillId)1229 private View findView(@NonNull AutofillId autofillId) { 1230 final AutofillClient client = getClientLocked(); 1231 1232 if (client == null) { 1233 return null; 1234 } 1235 1236 return client.findViewByAutofillIdTraversal(autofillId.getViewId()); 1237 } 1238 1239 /** @hide */ hasAutofillFeature()1240 public boolean hasAutofillFeature() { 1241 return mService != null; 1242 } 1243 post(Runnable runnable)1244 private void post(Runnable runnable) { 1245 final AutofillClient client = getClientLocked(); 1246 if (client == null) { 1247 if (sVerbose) Log.v(TAG, "ignoring post() because client is null"); 1248 return; 1249 } 1250 client.runOnUiThread(runnable); 1251 } 1252 1253 /** 1254 * View tracking information. Once all tracked views become invisible the session is finished. 1255 */ 1256 private class TrackedViews { 1257 /** Visible tracked views */ 1258 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; 1259 1260 /** Invisible tracked views */ 1261 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; 1262 1263 /** 1264 * Check if set is null or value is in set. 1265 * 1266 * @param set The set or null (== empty set) 1267 * @param value The value that might be in the set 1268 * 1269 * @return {@code true} iff set is not empty and value is in set 1270 */ 1271 // TODO: move to Helper as static method isInSet(@ullable ArraySet<T> set, T value)1272 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) { 1273 return set != null && set.contains(value); 1274 } 1275 1276 /** 1277 * Add a value to a set. If set is null, create a new set. 1278 * 1279 * @param set The set or null (== empty set) 1280 * @param valueToAdd The value to add 1281 * 1282 * @return The set including the new value. If set was {@code null}, a set containing only 1283 * the new value. 1284 */ 1285 // TODO: move to Helper as static method 1286 @NonNull addToSet(@ullable ArraySet<T> set, T valueToAdd)1287 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) { 1288 if (set == null) { 1289 set = new ArraySet<>(1); 1290 } 1291 1292 set.add(valueToAdd); 1293 1294 return set; 1295 } 1296 1297 /** 1298 * Remove a value from a set. 1299 * 1300 * @param set The set or null (== empty set) 1301 * @param valueToRemove The value to remove 1302 * 1303 * @return The set without the removed value. {@code null} if set was null, or is empty 1304 * after removal. 1305 */ 1306 // TODO: move to Helper as static method 1307 @Nullable removeFromSet(@ullable ArraySet<T> set, T valueToRemove)1308 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) { 1309 if (set == null) { 1310 return null; 1311 } 1312 1313 set.remove(valueToRemove); 1314 1315 if (set.isEmpty()) { 1316 return null; 1317 } 1318 1319 return set; 1320 } 1321 1322 /** 1323 * Set the tracked views. 1324 * 1325 * @param trackedIds The views to be tracked 1326 */ TrackedViews(@ullable AutofillId[] trackedIds)1327 TrackedViews(@Nullable AutofillId[] trackedIds) { 1328 final AutofillClient client = getClientLocked(); 1329 if (trackedIds != null && client != null) { 1330 final boolean[] isVisible; 1331 1332 if (client.isVisibleForAutofill()) { 1333 isVisible = client.getViewVisibility(getViewIds(trackedIds)); 1334 } else { 1335 // All false 1336 isVisible = new boolean[trackedIds.length]; 1337 } 1338 1339 final int numIds = trackedIds.length; 1340 for (int i = 0; i < numIds; i++) { 1341 final AutofillId id = trackedIds[i]; 1342 1343 if (isVisible[i]) { 1344 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1345 } else { 1346 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1347 } 1348 } 1349 } 1350 1351 if (sVerbose) { 1352 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " 1353 + " mVisibleTrackedIds=" + mVisibleTrackedIds 1354 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); 1355 } 1356 1357 if (mVisibleTrackedIds == null) { 1358 finishSessionLocked(); 1359 } 1360 } 1361 1362 /** 1363 * Called when a {@link View view's} visibility changes. 1364 * 1365 * @param view {@link View} that was exited. 1366 * @param isVisible visible if the view is visible in the view hierarchy. 1367 */ notifyViewVisibilityChange(@onNull View view, boolean isVisible)1368 void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { 1369 AutofillId id = getAutofillId(view); 1370 AutofillClient client = getClientLocked(); 1371 1372 if (sDebug) { 1373 Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible=" 1374 + isVisible); 1375 } 1376 1377 if (client != null && client.isVisibleForAutofill()) { 1378 if (isVisible) { 1379 if (isInSet(mInvisibleTrackedIds, id)) { 1380 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); 1381 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); 1382 } 1383 } else { 1384 if (isInSet(mVisibleTrackedIds, id)) { 1385 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); 1386 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); 1387 } 1388 } 1389 } 1390 1391 if (mVisibleTrackedIds == null) { 1392 if (sVerbose) { 1393 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds); 1394 } 1395 finishSessionLocked(); 1396 } 1397 } 1398 1399 /** 1400 * Called once the client becomes visible. 1401 * 1402 * @see AutofillClient#isVisibleForAutofill() 1403 */ onVisibleForAutofillLocked()1404 void onVisibleForAutofillLocked() { 1405 // The visibility of the views might have changed while the client was not be visible, 1406 // hence update the visibility state for all views. 1407 AutofillClient client = getClientLocked(); 1408 ArraySet<AutofillId> updatedVisibleTrackedIds = null; 1409 ArraySet<AutofillId> updatedInvisibleTrackedIds = null; 1410 if (client != null) { 1411 if (mInvisibleTrackedIds != null) { 1412 final ArrayList<AutofillId> orderedInvisibleIds = 1413 new ArrayList<>(mInvisibleTrackedIds); 1414 final boolean[] isVisible = client.getViewVisibility( 1415 getViewIds(orderedInvisibleIds)); 1416 1417 final int numInvisibleTrackedIds = orderedInvisibleIds.size(); 1418 for (int i = 0; i < numInvisibleTrackedIds; i++) { 1419 final AutofillId id = orderedInvisibleIds.get(i); 1420 if (isVisible[i]) { 1421 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1422 1423 if (sDebug) { 1424 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible"); 1425 } 1426 } else { 1427 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1428 } 1429 } 1430 } 1431 1432 if (mVisibleTrackedIds != null) { 1433 final ArrayList<AutofillId> orderedVisibleIds = 1434 new ArrayList<>(mVisibleTrackedIds); 1435 final boolean[] isVisible = client.getViewVisibility( 1436 getViewIds(orderedVisibleIds)); 1437 1438 final int numVisibleTrackedIds = orderedVisibleIds.size(); 1439 for (int i = 0; i < numVisibleTrackedIds; i++) { 1440 final AutofillId id = orderedVisibleIds.get(i); 1441 1442 if (isVisible[i]) { 1443 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); 1444 } else { 1445 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); 1446 1447 if (sDebug) { 1448 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible"); 1449 } 1450 } 1451 } 1452 } 1453 1454 mInvisibleTrackedIds = updatedInvisibleTrackedIds; 1455 mVisibleTrackedIds = updatedVisibleTrackedIds; 1456 } 1457 1458 if (mVisibleTrackedIds == null) { 1459 finishSessionLocked(); 1460 } 1461 } 1462 } 1463 1464 /** 1465 * Callback for auto-fill related events. 1466 * 1467 * <p>Typically used for applications that display their own "auto-complete" views, so they can 1468 * enable / disable such views when the auto-fill UI affordance is shown / hidden. 1469 */ 1470 public abstract static class AutofillCallback { 1471 1472 /** @hide */ 1473 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN}) 1474 @Retention(RetentionPolicy.SOURCE) 1475 public @interface AutofillEventType {} 1476 1477 /** 1478 * The auto-fill input UI affordance associated with the view was shown. 1479 * 1480 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it 1481 * should be hidden upon receiving this event. 1482 */ 1483 public static final int EVENT_INPUT_SHOWN = 1; 1484 1485 /** 1486 * The auto-fill input UI affordance associated with the view was hidden. 1487 * 1488 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a 1489 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now. 1490 */ 1491 public static final int EVENT_INPUT_HIDDEN = 2; 1492 1493 /** 1494 * The auto-fill input UI affordance associated with the view won't be shown because 1495 * autofill is not available. 1496 * 1497 * <p>If the view provides its own auto-complete UI affordance but was not displaying it 1498 * to avoid flickering, it could shown it upon receiving this event. 1499 */ 1500 public static final int EVENT_INPUT_UNAVAILABLE = 3; 1501 1502 /** 1503 * Called after a change in the autofill state associated with a view. 1504 * 1505 * @param view view associated with the change. 1506 * 1507 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 1508 */ onAutofillEvent(@onNull View view, @AutofillEventType int event)1509 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) { 1510 } 1511 1512 /** 1513 * Called after a change in the autofill state associated with a virtual view. 1514 * 1515 * @param view parent view associated with the change. 1516 * @param virtualId id identifying the virtual child inside the parent view. 1517 * 1518 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}. 1519 */ onAutofillEvent(@onNull View view, int virtualId, @AutofillEventType int event)1520 public void onAutofillEvent(@NonNull View view, int virtualId, 1521 @AutofillEventType int event) { 1522 } 1523 } 1524 1525 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub { 1526 private final WeakReference<AutofillManager> mAfm; 1527 AutofillManagerClient(AutofillManager autofillManager)1528 AutofillManagerClient(AutofillManager autofillManager) { 1529 mAfm = new WeakReference<>(autofillManager); 1530 } 1531 1532 @Override setState(boolean enabled, boolean resetSession, boolean resetClient)1533 public void setState(boolean enabled, boolean resetSession, boolean resetClient) { 1534 final AutofillManager afm = mAfm.get(); 1535 if (afm != null) { 1536 afm.post(() -> afm.setState(enabled, resetSession, resetClient)); 1537 } 1538 } 1539 1540 @Override autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values)1541 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) { 1542 final AutofillManager afm = mAfm.get(); 1543 if (afm != null) { 1544 afm.post(() -> afm.autofill(sessionId, ids, values)); 1545 } 1546 } 1547 1548 @Override authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent)1549 public void authenticate(int sessionId, int authenticationId, IntentSender intent, 1550 Intent fillInIntent) { 1551 final AutofillManager afm = mAfm.get(); 1552 if (afm != null) { 1553 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent)); 1554 } 1555 } 1556 1557 @Override requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)1558 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height, 1559 Rect anchorBounds, IAutofillWindowPresenter presenter) { 1560 final AutofillManager afm = mAfm.get(); 1561 if (afm != null) { 1562 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds, 1563 presenter)); 1564 } 1565 } 1566 1567 @Override requestHideFillUi(int sessionId, AutofillId id)1568 public void requestHideFillUi(int sessionId, AutofillId id) { 1569 final AutofillManager afm = mAfm.get(); 1570 if (afm != null) { 1571 afm.post(() -> afm.requestHideFillUi(id)); 1572 } 1573 } 1574 1575 @Override notifyNoFillUi(int sessionId, AutofillId id)1576 public void notifyNoFillUi(int sessionId, AutofillId id) { 1577 final AutofillManager afm = mAfm.get(); 1578 if (afm != null) { 1579 afm.post(() -> afm.notifyNoFillUi(sessionId, id)); 1580 } 1581 } 1582 1583 @Override startIntentSender(IntentSender intentSender)1584 public void startIntentSender(IntentSender intentSender) { 1585 final AutofillManager afm = mAfm.get(); 1586 if (afm != null) { 1587 afm.post(() -> { 1588 try { 1589 afm.mContext.startIntentSender(intentSender, null, 0, 0, 0); 1590 } catch (IntentSender.SendIntentException e) { 1591 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e); 1592 } 1593 }); 1594 } 1595 } 1596 1597 @Override setTrackedViews(int sessionId, AutofillId[] ids, boolean saveOnAllViewsInvisible, AutofillId[] fillableIds)1598 public void setTrackedViews(int sessionId, AutofillId[] ids, 1599 boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) { 1600 final AutofillManager afm = mAfm.get(); 1601 if (afm != null) { 1602 afm.post(() -> 1603 afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds) 1604 ); 1605 } 1606 } 1607 } 1608 } 1609