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