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