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