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.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
21 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
22 import static android.view.autofill.Helper.sDebug;
23 import static android.view.autofill.Helper.sVerbose;
24 import static android.view.autofill.Helper.toList;
25 
26 import android.accessibilityservice.AccessibilityServiceInfo;
27 import android.annotation.IntDef;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.RequiresFeature;
31 import android.annotation.SystemApi;
32 import android.annotation.SystemService;
33 import android.annotation.TestApi;
34 import android.content.AutofillOptions;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentSender;
39 import android.content.pm.PackageManager;
40 import android.content.pm.ResolveInfo;
41 import android.graphics.Rect;
42 import android.metrics.LogMaker;
43 import android.os.Build;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.IBinder;
47 import android.os.Looper;
48 import android.os.Parcelable;
49 import android.os.RemoteException;
50 import android.os.SystemClock;
51 import android.service.autofill.AutofillService;
52 import android.service.autofill.FillEventHistory;
53 import android.service.autofill.UserData;
54 import android.text.TextUtils;
55 import android.util.ArrayMap;
56 import android.util.ArraySet;
57 import android.util.DebugUtils;
58 import android.util.Log;
59 import android.util.Slog;
60 import android.util.SparseArray;
61 import android.view.Choreographer;
62 import android.view.KeyEvent;
63 import android.view.View;
64 import android.view.accessibility.AccessibilityEvent;
65 import android.view.accessibility.AccessibilityManager;
66 import android.view.accessibility.AccessibilityNodeInfo;
67 import android.view.accessibility.AccessibilityNodeProvider;
68 import android.view.accessibility.AccessibilityWindowInfo;
69 import android.view.inputmethod.InputMethodManager;
70 import android.widget.EditText;
71 import android.widget.TextView;
72 
73 import com.android.internal.annotations.GuardedBy;
74 import com.android.internal.logging.MetricsLogger;
75 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
76 import com.android.internal.os.IResultReceiver;
77 import com.android.internal.util.ArrayUtils;
78 import com.android.internal.util.Preconditions;
79 import com.android.internal.util.SyncResultReceiver;
80 
81 import org.xmlpull.v1.XmlPullParserException;
82 
83 import java.io.IOException;
84 import java.io.PrintWriter;
85 import java.lang.annotation.Retention;
86 import java.lang.annotation.RetentionPolicy;
87 import java.lang.ref.WeakReference;
88 import java.util.ArrayList;
89 import java.util.Arrays;
90 import java.util.Collections;
91 import java.util.List;
92 import java.util.Objects;
93 import java.util.Set;
94 
95 import sun.misc.Cleaner;
96 
97 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
98 
99 /**
100  * <p>The {@link AutofillManager} class provides ways for apps and custom views to
101  * integrate with the Autofill Framework lifecycle.
102  *
103  * <p>To learn about using Autofill in your app, read
104  * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
105  *
106  * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
107  *
108  * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
109  * activity context. The autofill context is created when one of the following methods is called for
110  * the first time in an activity context, and the current user has an enabled autofill service:
111  *
112  * <ul>
113  *   <li>{@link #notifyViewEntered(View)}
114  *   <li>{@link #notifyViewEntered(View, int, Rect)}
115  *   <li>{@link #requestAutofill(View)}
116  * </ul>
117  *
118  * <p>Typically, the context is automatically created when the first view of the activity is
119  * focused because {@code View.onFocusChanged()} indirectly calls
120  * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
121  * explicitly create it (for example, a custom view developer could offer a contextual menu action
122  * in a text-field view to let users manually request autofill).
123  *
124  * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
125  * that represents the view hierarchy by calling
126  * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
127  * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
128  * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
129  * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
130  * the hierarchy.
131  *
132  * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
133  * parses it looking for views that can be autofilled. If the service finds such views, it returns
134  * a data structure to the Android System containing the following optional info:
135  *
136  * <ul>
137  *   <li>Datasets used to autofill subsets of views in the activity.
138  *   <li>Id of views that the service can save their values for future autofilling.
139  * </ul>
140  *
141  * <p>When the service returns datasets, the Android System displays an autofill dataset picker
142  * UI associated with the view, when the view is focused on and is part of a dataset.
143  * The application can be notified when the UI is shown by registering an
144  * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
145  * selects a dataset from the UI, all views present in the dataset are autofilled, through
146  * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
147  *
148  * <p>When the service returns ids of savable views, the Android System keeps track of changes
149  * made to these views, so they can be used to determine if the autofill save UI is shown later.
150  *
151  * <p>The context is then finished when one of the following occurs:
152  *
153  * <ul>
154  *   <li>{@link #commit()} is called or all savable views are gone.
155  *   <li>{@link #cancel()} is called.
156  * </ul>
157  *
158  * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
159  * shows an autofill save UI if the value of savable views have changed. If the user selects the
160  * option to Save, the current value of the views is then sent to the autofill service.
161  *
162  * <h3 id="additional-notes">Additional notes</h3>
163  *
164  * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
165  */
166 @SystemService(Context.AUTOFILL_MANAGER_SERVICE)
167 @RequiresFeature(PackageManager.FEATURE_AUTOFILL)
168 public final class AutofillManager {
169 
170     private static final String TAG = "AutofillManager";
171 
172     /**
173      * Intent extra: The assist structure which captures the filled screen.
174      *
175      * <p>
176      * Type: {@link android.app.assist.AssistStructure}
177      */
178     public static final String EXTRA_ASSIST_STRUCTURE =
179             "android.view.autofill.extra.ASSIST_STRUCTURE";
180 
181     /**
182      * Intent extra: The result of an authentication operation. It is
183      * either a fully populated {@link android.service.autofill.FillResponse}
184      * or a fully populated {@link android.service.autofill.Dataset} if
185      * a response or a dataset is being authenticated respectively.
186      *
187      * <p>
188      * Type: {@link android.service.autofill.FillResponse} or a
189      * {@link android.service.autofill.Dataset}
190      */
191     public static final String EXTRA_AUTHENTICATION_RESULT =
192             "android.view.autofill.extra.AUTHENTICATION_RESULT";
193 
194     /**
195      * Intent extra: The optional extras provided by the
196      * {@link android.service.autofill.AutofillService}.
197      *
198      * <p>For example, when the service responds to a {@link
199      * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
200      * a {@code FillResponse} that requires authentication, the Intent that launches the
201      * service authentication will contain the Bundle set by
202      * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
203      *
204      * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
205      * can also add this bundle to the {@link Intent} set as the
206      * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
207      * so the bundle can be recovered later on
208      * {@link android.service.autofill.SaveRequest#getClientState()}.
209      *
210      * <p>
211      * Type: {@link android.os.Bundle}
212      */
213     public static final String EXTRA_CLIENT_STATE =
214             "android.view.autofill.extra.CLIENT_STATE";
215 
216     /** @hide */
217     public static final String EXTRA_RESTORE_SESSION_TOKEN =
218             "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
219 
220     /** @hide */
221     public static final String EXTRA_RESTORE_CROSS_ACTIVITY =
222             "android.view.autofill.extra.RESTORE_CROSS_ACTIVITY";
223 
224     /**
225      * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
226      *
227      * @hide
228      */
229     public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
230             "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
231 
232     private static final String SESSION_ID_TAG = "android:sessionId";
233     private static final String STATE_TAG = "android:state";
234     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
235 
236     /** @hide */ public static final int ACTION_START_SESSION = 1;
237     /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
238     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
239     /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
240     /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
241 
242     /** @hide */ public static final int NO_LOGGING = 0;
243     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
244     /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
245     /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
246     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
247 
248     // NOTE: flag below is used by the session start receiver only, hence it can have values above
249     /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
250 
251     /** @hide */
252     public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
253             ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
254             : AutofillManager.NO_LOGGING;
255 
256     /** @hide */
257     public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
258 
259     /** Which bits in an authentication id are used for the dataset id */
260     private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
261     /** How many bits in an authentication id are used for the dataset id */
262     private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
263     /** @hide The index for an undefined data set */
264     public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
265 
266     /**
267      * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
268      *
269      * @hide
270      */
271     public static final int PENDING_UI_OPERATION_CANCEL = 1;
272 
273     /**
274      * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
275      *
276      * @hide
277      */
278     public static final int PENDING_UI_OPERATION_RESTORE = 2;
279 
280     /**
281      * Initial state of the autofill context, set when there is no session (i.e., when
282      * {@link #mSessionId} is {@link #NO_SESSION}).
283      *
284      * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
285      * the server.
286      *
287      * @hide
288      */
289     public static final int STATE_UNKNOWN = 0;
290 
291     /**
292      * State where the autofill context hasn't been {@link #commit() finished} nor
293      * {@link #cancel() canceled} yet.
294      *
295      * @hide
296      */
297     public static final int STATE_ACTIVE = 1;
298 
299     /**
300      * State where the autofill context was finished by the server because the autofill
301      * service could not autofill the activity.
302      *
303      * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
304      * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
305      *
306      * @hide
307      */
308     public static final int STATE_FINISHED = 2;
309 
310     /**
311      * State where the autofill context has been {@link #commit() finished} but the server still has
312      * a session because the Save UI hasn't been dismissed yet.
313      *
314      * @hide
315      */
316     public static final int STATE_SHOWING_SAVE_UI = 3;
317 
318     /**
319      * State where the autofill is disabled because the service cannot autofill the activity at all.
320      *
321      * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
322      * (and {@link #requestAutofill(View, int, Rect)}).
323      *
324      * @hide
325      */
326     public static final int STATE_DISABLED_BY_SERVICE = 4;
327 
328     /**
329      * Same as {@link #STATE_UNKNOWN}, but used on
330      * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
331      * because the URL bar changed on client mode
332      *
333      * @hide
334      */
335     public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
336 
337     /**
338      * Same as {@link #STATE_UNKNOWN}, but used on
339      * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
340      * because the service failed to fullfil a request.
341      *
342      * @hide
343      */
344     public static final int STATE_UNKNOWN_FAILED = 6;
345 
346     /**
347      * Timeout in ms for calls to the field classification service.
348      * @hide
349      */
350     public static final int FC_SERVICE_TIMEOUT = 5000;
351 
352     /**
353      * Timeout for calls to system_server.
354      */
355     private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
356 
357     /**
358      * @hide
359      */
360     @TestApi
361     public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
362 
363     /**
364      * Disables Augmented Autofill.
365      *
366      * @hide
367      */
368     @TestApi
369     public static final int FLAG_SMART_SUGGESTION_OFF = 0x0;
370 
371     /**
372      * Displays the Augment Autofill window using the same mechanism (such as a popup-window
373      * attached to the focused view) as the standard autofill.
374      *
375      * @hide
376      */
377     @TestApi
378     public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
379 
380     /** @hide */
381     @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM })
382     @Retention(RetentionPolicy.SOURCE)
383     public @interface SmartSuggestionMode {}
384 
385     /**
386      * {@code DeviceConfig} property used to set which Smart Suggestion modes for Augmented Autofill
387      * are available.
388      *
389      * @hide
390      */
391     @TestApi
392     public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
393             "smart_suggestion_supported_modes";
394 
395     /**
396      * Sets how long (in ms) the augmented autofill service is bound while idle.
397      *
398      * <p>Use {@code 0} to keep it permanently bound.
399      *
400      * @hide
401      */
402     public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
403             "augmented_service_idle_unbind_timeout";
404 
405     /**
406      * Sets how long (in ms) the augmented autofill service request is killed if not replied.
407      *
408      * @hide
409      */
410     public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
411             "augmented_service_request_timeout";
412 
413     /** @hide */
414     public static final int RESULT_OK = 0;
415     /** @hide */
416     public static final int RESULT_CODE_NOT_SERVICE = -1;
417 
418     /**
419      * Makes an authentication id from a request id and a dataset id.
420      *
421      * @param requestId The request id.
422      * @param datasetId The dataset id.
423      * @return The authentication id.
424      * @hide
425      */
makeAuthenticationId(int requestId, int datasetId)426     public static int makeAuthenticationId(int requestId, int datasetId) {
427         return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
428                 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
429     }
430 
431     /**
432      * Gets the request id from an authentication id.
433      *
434      * @param authRequestId The authentication id.
435      * @return The request id.
436      * @hide
437      */
getRequestIdFromAuthenticationId(int authRequestId)438     public static int getRequestIdFromAuthenticationId(int authRequestId) {
439         return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
440     }
441 
442     /**
443      * Gets the dataset id from an authentication id.
444      *
445      * @param authRequestId The authentication id.
446      * @return The dataset id.
447      * @hide
448      */
getDatasetIdFromAuthenticationId(int authRequestId)449     public static int getDatasetIdFromAuthenticationId(int authRequestId) {
450         return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
451     }
452 
453     private final MetricsLogger mMetricsLogger = new MetricsLogger();
454 
455     /**
456      * There is currently no session running.
457      * {@hide}
458      */
459     public static final int NO_SESSION = Integer.MAX_VALUE;
460 
461     private final IAutoFillManager mService;
462 
463     private final Object mLock = new Object();
464 
465     @GuardedBy("mLock")
466     private IAutoFillManagerClient mServiceClient;
467 
468     @GuardedBy("mLock")
469     private Cleaner mServiceClientCleaner;
470 
471     @GuardedBy("mLock")
472     private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
473 
474     @GuardedBy("mLock")
475     private AutofillCallback mCallback;
476 
477     private final Context mContext;
478 
479     @GuardedBy("mLock")
480     private int mSessionId = NO_SESSION;
481 
482     @GuardedBy("mLock")
483     private int mState = STATE_UNKNOWN;
484 
485     @GuardedBy("mLock")
486     private boolean mEnabled;
487 
488     /** If a view changes to this mapping the autofill operation was successful */
489     @GuardedBy("mLock")
490     @Nullable private ParcelableMap mLastAutofilledData;
491 
492     /** If view tracking is enabled, contains the tracking state */
493     @GuardedBy("mLock")
494     @Nullable private TrackedViews mTrackedViews;
495 
496     /** Views that are only tracked because they are fillable and could be anchoring the UI. */
497     @GuardedBy("mLock")
498     @Nullable private ArraySet<AutofillId> mFillableIds;
499 
500     /** id of last requested autofill ui */
501     @Nullable private AutofillId mIdShownFillUi;
502 
503     /**
504      * Views that were already "entered" - if they're entered again when the session is not active,
505      * they're ignored
506      * */
507     @GuardedBy("mLock")
508     @Nullable private ArraySet<AutofillId> mEnteredIds;
509 
510     /**
511      * Views that were otherwised not important for autofill but triggered a session because the
512      * context is whitelisted for augmented autofill.
513      */
514     @GuardedBy("mLock")
515     @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
516 
517     /** If set, session is commited when the field is clicked. */
518     @GuardedBy("mLock")
519     @Nullable private AutofillId mSaveTriggerId;
520 
521     /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
522     @GuardedBy("mLock")
523     private boolean mOnInvisibleCalled;
524 
525     /** If set, session is commited when the activity is finished; otherwise session is canceled. */
526     @GuardedBy("mLock")
527     private boolean mSaveOnFinish;
528 
529     /** If compatibility mode is enabled - this is a bridge to interact with a11y */
530     @GuardedBy("mLock")
531     private CompatibilityBridge mCompatibilityBridge;
532 
533     @Nullable
534     private final AutofillOptions mOptions;
535 
536     /** When set, session is only used for augmented autofill requests. */
537     @GuardedBy("mLock")
538     private boolean mForAugmentedAutofillOnly;
539 
540     /**
541      * When set, standard autofill is disabled, but sessions can still be created for augmented
542      * autofill only.
543      */
544     @GuardedBy("mLock")
545     private boolean mEnabledForAugmentedAutofillOnly;
546 
547     /** @hide */
548     public interface AutofillClient {
549         /**
550          * Asks the client to start an authentication flow.
551          *
552          * @param authenticationId A unique id of the authentication operation.
553          * @param intent The authentication intent.
554          * @param fillInIntent The authentication fill-in intent.
555          */
autofillClientAuthenticate(int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)556         void autofillClientAuthenticate(int authenticationId, IntentSender intent,
557                 Intent fillInIntent, boolean authenticateInline);
558 
559         /**
560          * Tells the client this manager has state to be reset.
561          */
autofillClientResetableStateAvailable()562         void autofillClientResetableStateAvailable();
563 
564         /**
565          * Request showing the autofill UI.
566          *
567          * @param anchor The real view the UI needs to anchor to.
568          * @param width The width of the fill UI content.
569          * @param height The height of the fill UI content.
570          * @param virtualBounds The bounds of the virtual decendant of the anchor.
571          * @param presenter The presenter that controls the fill UI window.
572          * @return Whether the UI was shown.
573          */
autofillClientRequestShowFillUi(@onNull View anchor, int width, int height, @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter)574         boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
575                 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
576 
577         /**
578          * Dispatch unhandled keyevent from Autofill window
579          * @param anchor The real view the UI needs to anchor to.
580          * @param keyEvent Unhandled KeyEvent from autofill window.
581          */
autofillClientDispatchUnhandledKey(@onNull View anchor, @NonNull KeyEvent keyEvent)582         void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
583 
584         /**
585          * Request hiding the autofill UI.
586          *
587          * @return Whether the UI was hidden.
588          */
autofillClientRequestHideFillUi()589         boolean autofillClientRequestHideFillUi();
590 
591         /**
592          * Gets whether the fill UI is currenlty being shown.
593          *
594          * @return Whether the fill UI is currently being shown
595          */
autofillClientIsFillUiShowing()596         boolean autofillClientIsFillUiShowing();
597 
598         /**
599          * Checks if views are currently attached and visible.
600          *
601          * @return And array with {@code true} iff the view is attached or visible
602          */
autofillClientGetViewVisibility(@onNull AutofillId[] autofillIds)603         @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
604 
605         /**
606          * Checks is the client is currently visible as understood by autofill.
607          *
608          * @return {@code true} if the client is currently visible
609          */
autofillClientIsVisibleForAutofill()610         boolean autofillClientIsVisibleForAutofill();
611 
612         /**
613          * Client might disable enter/exit event e.g. when activity is paused.
614          */
isDisablingEnterExitEventForAutofill()615         boolean isDisablingEnterExitEventForAutofill();
616 
617         /**
618          * Finds views by traversing the hierarchies of the client.
619          *
620          * @param autofillIds The autofill ids of the views to find
621          *
622          * @return And array containing the views (empty if no views found).
623          */
autofillClientFindViewsByAutofillIdTraversal( @onNull AutofillId[] autofillIds)624         @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
625                 @NonNull AutofillId[] autofillIds);
626 
627         /**
628          * Finds a view by traversing the hierarchies of the client.
629          *
630          * @param autofillId The autofill id of the views to find
631          *
632          * @return The view, or {@code null} if not found
633          */
autofillClientFindViewByAutofillIdTraversal(@onNull AutofillId autofillId)634         @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
635 
636         /**
637          * Finds a view by a11y id in a given client window.
638          *
639          * @param viewId The accessibility id of the views to find
640          * @param windowId The accessibility window id where to search
641          *
642          * @return The view, or {@code null} if not found
643          */
autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId)644         @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
645 
646         /**
647          * Runs the specified action on the UI thread.
648          */
autofillClientRunOnUiThread(Runnable action)649         void autofillClientRunOnUiThread(Runnable action);
650 
651         /**
652          * Gets the complete component name of this client.
653          */
autofillClientGetComponentName()654         ComponentName autofillClientGetComponentName();
655 
656         /**
657          * Gets the activity token
658          */
autofillClientGetActivityToken()659         @Nullable IBinder autofillClientGetActivityToken();
660 
661         /**
662           * @return Whether compatibility mode is enabled.
663           */
autofillClientIsCompatibilityModeEnabled()664         boolean autofillClientIsCompatibilityModeEnabled();
665 
666         /**
667          * Gets the next unique autofill ID.
668          *
669          * <p>Typically used to manage views whose content is recycled - see
670          * {@link View#setAutofillId(AutofillId)} for more info.
671          *
672          * @return An ID that is unique in the activity.
673          */
autofillClientGetNextAutofillId()674         @Nullable AutofillId autofillClientGetNextAutofillId();
675     }
676 
677     /**
678      * @hide
679      */
AutofillManager(Context context, IAutoFillManager service)680     public AutofillManager(Context context, IAutoFillManager service) {
681         mContext = Preconditions.checkNotNull(context, "context cannot be null");
682         mService = service;
683         mOptions = context.getAutofillOptions();
684 
685         if (mOptions != null) {
686             sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
687             sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
688         }
689     }
690 
691     /**
692      * @hide
693      */
enableCompatibilityMode()694     public void enableCompatibilityMode() {
695         synchronized (mLock) {
696             // The accessibility manager is a singleton so we may need to plug
697             // different bridge based on which activity is currently focused
698             // in the current process. Since compat would be rarely used, just
699             // create and register a new instance every time.
700             if (sDebug) {
701                 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
702             }
703             mCompatibilityBridge = new CompatibilityBridge();
704         }
705     }
706 
707     /**
708      * Restore state after activity lifecycle
709      *
710      * @param savedInstanceState The state to be restored
711      *
712      * {@hide}
713      */
onCreate(Bundle savedInstanceState)714     public void onCreate(Bundle savedInstanceState) {
715         if (!hasAutofillFeature()) {
716             return;
717         }
718         synchronized (mLock) {
719             mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
720 
721             if (isActiveLocked()) {
722                 Log.w(TAG, "New session was started before onCreate()");
723                 return;
724             }
725 
726             mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
727             mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
728 
729             if (mSessionId != NO_SESSION) {
730                 ensureServiceClientAddedIfNeededLocked();
731 
732                 final AutofillClient client = getClient();
733                 if (client != null) {
734                     final SyncResultReceiver receiver = new SyncResultReceiver(
735                             SYNC_CALLS_TIMEOUT_MS);
736                     try {
737                         mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
738                                 mServiceClient.asBinder(), receiver);
739                         final boolean sessionWasRestored = receiver.getIntResult() == 1;
740 
741                         if (!sessionWasRestored) {
742                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
743                             mSessionId = NO_SESSION;
744                             mState = STATE_UNKNOWN;
745                         } else {
746                             if (sDebug) {
747                                 Log.d(TAG, "session " + mSessionId + " was restored");
748                             }
749 
750                             client.autofillClientResetableStateAvailable();
751                         }
752                     } catch (RemoteException e) {
753                         Log.e(TAG, "Could not figure out if there was an autofill session", e);
754                     } catch (SyncResultReceiver.TimeoutException e) {
755                         Log.e(TAG, "Fail to get session restore status: " + e);
756                     }
757                 }
758             }
759         }
760     }
761 
762     /**
763      * Called once the client becomes visible.
764      *
765      * @see AutofillClient#autofillClientIsVisibleForAutofill()
766      *
767      * {@hide}
768      */
onVisibleForAutofill()769     public void onVisibleForAutofill() {
770         // This gets called when the client just got visible at which point the visibility
771         // of the tracked views may not have been computed (due to a pending layout, etc).
772         // While generally we have no way to know when the UI has settled. We will evaluate
773         // the tracked views state at the end of next frame to guarantee that everything
774         // that may need to be laid out is laid out.
775         Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
776             synchronized (mLock) {
777                 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
778                     mTrackedViews.onVisibleForAutofillChangedLocked();
779                 }
780             }
781         }, null);
782     }
783 
784     /**
785      * Called once the client becomes invisible.
786      *
787      * @see AutofillClient#autofillClientIsVisibleForAutofill()
788      *
789      * @param isExpiredResponse The response has expired or not
790      *
791      * {@hide}
792      */
onInvisibleForAutofill(boolean isExpiredResponse)793     public void onInvisibleForAutofill(boolean isExpiredResponse) {
794         synchronized (mLock) {
795             mOnInvisibleCalled = true;
796 
797             if (isExpiredResponse) {
798                 // Notify service the response has expired.
799                 updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
800                         ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
801             }
802         }
803     }
804 
805     /**
806      * Save state before activity lifecycle
807      *
808      * @param outState Place to store the state
809      *
810      * {@hide}
811      */
onSaveInstanceState(Bundle outState)812     public void onSaveInstanceState(Bundle outState) {
813         if (!hasAutofillFeature()) {
814             return;
815         }
816         synchronized (mLock) {
817             if (mSessionId != NO_SESSION) {
818                 outState.putInt(SESSION_ID_TAG, mSessionId);
819             }
820             if (mState != STATE_UNKNOWN) {
821                 outState.putInt(STATE_TAG, mState);
822             }
823             if (mLastAutofilledData != null) {
824                 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
825             }
826         }
827     }
828 
829     /**
830      * @hide
831      */
832     @GuardedBy("mLock")
isCompatibilityModeEnabledLocked()833     public boolean isCompatibilityModeEnabledLocked() {
834         return mCompatibilityBridge != null;
835     }
836 
837     /**
838      * Checks whether autofill is enabled for the current user.
839      *
840      * <p>Typically used to determine whether the option to explicitly request autofill should
841      * be offered - see {@link #requestAutofill(View)}.
842      *
843      * @return whether autofill is enabled for the current user.
844      */
isEnabled()845     public boolean isEnabled() {
846         if (!hasAutofillFeature()) {
847             return false;
848         }
849         synchronized (mLock) {
850             if (isDisabledByServiceLocked()) {
851                 return false;
852             }
853             ensureServiceClientAddedIfNeededLocked();
854             return mEnabled;
855         }
856     }
857 
858     /**
859      * Should always be called from {@link AutofillService#getFillEventHistory()}.
860      *
861      * @hide
862      */
getFillEventHistory()863     @Nullable public FillEventHistory getFillEventHistory() {
864         try {
865             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
866             mService.getFillEventHistory(receiver);
867             return receiver.getParcelableResult();
868         } catch (RemoteException e) {
869             throw e.rethrowFromSystemServer();
870         } catch (SyncResultReceiver.TimeoutException e) {
871             Log.e(TAG, "Fail to get fill event history: " + e);
872             return null;
873         }
874     }
875 
876     /**
877      * Explicitly requests a new autofill context.
878      *
879      * <p>Normally, the autofill context is automatically started if necessary when
880      * {@link #notifyViewEntered(View)} is called, but this method should be used in the
881      * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
882      * option on its contextual overflow menu, and the user selects it.
883      *
884      * @param view view requesting the new autofill context.
885      */
requestAutofill(@onNull View view)886     public void requestAutofill(@NonNull View view) {
887         int flags = FLAG_MANUAL_REQUEST;
888         if (!view.isFocused()) {
889             flags |= FLAG_VIEW_NOT_FOCUSED;
890         }
891         notifyViewEntered(view, flags);
892     }
893 
894     /**
895      * Explicitly cancels the current session and requests a new autofill context.
896      *
897      * <p>Normally, the autofill context is automatically started if necessary when
898      * {@link #notifyViewEntered(View)} is called, but this method should be used in
899      * cases where it must be explicitly started or restarted. Currently, this method should only
900      * be called by
901      * {@link android.service.autofill.augmented.AugmentedAutofillService#requestAutofill(
902      * ComponentName, AutofillId)} to cancel the current session and trigger the autofill flow in
903      * a new session, giving the autofill service or the augmented autofill service a chance to
904      * send updated suggestions.
905      *
906      * @param view view requesting the new autofill context.
907      */
requestAutofillFromNewSession(@onNull View view)908     void requestAutofillFromNewSession(@NonNull View view) {
909         cancel();
910         notifyViewEntered(view);
911     }
912 
913     /**
914      * Explicitly requests a new autofill context for virtual views.
915      *
916      * <p>Normally, the autofill context is automatically started if necessary when
917      * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
918      * cases where it must be explicitly started. For example, when the virtual view offers an
919      * AUTOFILL option on its contextual overflow menu, and the user selects it.
920      *
921      * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
922      * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
923      * the absolute bounds could be calculated by:
924      *
925      * <pre class="prettyprint">
926      *   int offset[] = new int[2];
927      *   getLocationOnScreen(offset);
928      *   Rect absBounds = new Rect(bounds.left + offset[0],
929      *       bounds.top + offset[1],
930      *       bounds.right + offset[0], bounds.bottom + offset[1]);
931      * </pre>
932      *
933      * @param view the virtual view parent.
934      * @param virtualId id identifying the virtual child inside the parent view.
935      * @param absBounds absolute boundaries of the virtual view in the screen.
936      */
requestAutofill(@onNull View view, int virtualId, @NonNull Rect absBounds)937     public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
938         int flags = FLAG_MANUAL_REQUEST;
939         if (!view.isFocused()) {
940             flags |= FLAG_VIEW_NOT_FOCUSED;
941         }
942         notifyViewEntered(view, virtualId, absBounds, flags);
943     }
944 
945     /**
946      * Called when a {@link View} that supports autofill is entered.
947      *
948      * @param view {@link View} that was entered.
949      */
notifyViewEntered(@onNull View view)950     public void notifyViewEntered(@NonNull View view) {
951         notifyViewEntered(view, 0);
952     }
953 
954     @GuardedBy("mLock")
shouldIgnoreViewEnteredLocked(@onNull AutofillId id, int flags)955     private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
956         if (isDisabledByServiceLocked()) {
957             if (sVerbose) {
958                 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
959                         + ") on state " + getStateAsStringLocked() + " because disabled by svc");
960             }
961             return true;
962         }
963         if (isFinishedLocked()) {
964             // Session already finished: ignore if automatic request and view already entered
965             if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
966                     && mEnteredIds.contains(id)) {
967                 if (sVerbose) {
968                     Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
969                             + ") on state " + getStateAsStringLocked()
970                             + " because view was already entered: " + mEnteredIds);
971                 }
972                 return true;
973             }
974         }
975         return false;
976     }
977 
isClientVisibleForAutofillLocked()978     private boolean isClientVisibleForAutofillLocked() {
979         final AutofillClient client = getClient();
980         return client != null && client.autofillClientIsVisibleForAutofill();
981     }
982 
isClientDisablingEnterExitEvent()983     private boolean isClientDisablingEnterExitEvent() {
984         final AutofillClient client = getClient();
985         return client != null && client.isDisablingEnterExitEventForAutofill();
986     }
987 
notifyViewEntered(@onNull View view, int flags)988     private void notifyViewEntered(@NonNull View view, int flags) {
989         if (!hasAutofillFeature()) {
990             return;
991         }
992         AutofillCallback callback;
993         synchronized (mLock) {
994             callback = notifyViewEnteredLocked(view, flags);
995         }
996 
997         if (callback != null) {
998             mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
999         }
1000     }
1001 
1002     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
1003     @GuardedBy("mLock")
notifyViewEnteredLocked(@onNull View view, int flags)1004     private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
1005         final AutofillId id = view.getAutofillId();
1006         if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
1007 
1008         AutofillCallback callback = null;
1009 
1010         ensureServiceClientAddedIfNeededLocked();
1011 
1012         if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
1013             if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1014 
1015             if (mCallback != null) {
1016                 callback = mCallback;
1017             }
1018         } else {
1019             // don't notify entered when Activity is already in background
1020             if (!isClientDisablingEnterExitEvent()) {
1021                 final AutofillValue value = view.getAutofillValue();
1022 
1023                 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1024                     flags |= FLAG_PASSWORD_INPUT_TYPE;
1025                 }
1026 
1027                 if (!isActiveLocked()) {
1028                     // Starts new session.
1029                     startSessionLocked(id, null, value, flags);
1030                 } else {
1031                     // Update focus on existing session.
1032                     if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1033                         if (sDebug) {
1034                             Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1035                                     + "mForAugmentedAutofillOnly on manual request");
1036                         }
1037                         mForAugmentedAutofillOnly = false;
1038                     }
1039                     updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
1040                 }
1041                 addEnteredIdLocked(id);
1042             }
1043         }
1044         return callback;
1045     }
1046 
1047     /**
1048      * Called when a {@link View} that supports autofill is exited.
1049      *
1050      * @param view {@link View} that was exited.
1051      */
notifyViewExited(@onNull View view)1052     public void notifyViewExited(@NonNull View view) {
1053         if (!hasAutofillFeature()) {
1054             return;
1055         }
1056         synchronized (mLock) {
1057             notifyViewExitedLocked(view);
1058         }
1059     }
1060 
1061     @GuardedBy("mLock")
notifyViewExitedLocked(@onNull View view)1062     void notifyViewExitedLocked(@NonNull View view) {
1063         ensureServiceClientAddedIfNeededLocked();
1064 
1065         if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
1066             // dont notify exited when Activity is already in background
1067             if (!isClientDisablingEnterExitEvent()) {
1068                 final AutofillId id = view.getAutofillId();
1069 
1070                 // Update focus on existing session.
1071                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
1072             }
1073         }
1074     }
1075 
1076     /**
1077      * Called when a {@link View view's} visibility changed.
1078      *
1079      * @param view {@link View} that was exited.
1080      * @param isVisible visible if the view is visible in the view hierarchy.
1081      */
notifyViewVisibilityChanged(@onNull View view, boolean isVisible)1082     public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1083         notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1084     }
1085 
1086     /**
1087      * Called when a virtual view's visibility changed.
1088      *
1089      * @param view {@link View} that was exited.
1090      * @param virtualId id identifying the virtual child inside the parent view.
1091      * @param isVisible visible if the view is visible in the view hierarchy.
1092      */
notifyViewVisibilityChanged(@onNull View view, int virtualId, boolean isVisible)1093     public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1094         notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1095     }
1096 
1097     /**
1098      * Called when a view/virtual view's visibility changed.
1099      *
1100      * @param view {@link View} that was exited.
1101      * @param virtualId id identifying the virtual child inside the parent view.
1102      * @param isVisible visible if the view is visible in the view hierarchy.
1103      * @param virtual Whether the view is virtual.
1104      */
notifyViewVisibilityChangedInternal(@onNull View view, int virtualId, boolean isVisible, boolean virtual)1105     private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1106             boolean isVisible, boolean virtual) {
1107         synchronized (mLock) {
1108             if (mForAugmentedAutofillOnly) {
1109                 if (sVerbose) {
1110                     Log.v(TAG,  "notifyViewVisibilityChanged(): ignoring on augmented only mode");
1111                 }
1112                 return;
1113             }
1114             if (mEnabled && isActiveLocked()) {
1115                 final AutofillId id = virtual ? getAutofillId(view, virtualId)
1116                         : view.getAutofillId();
1117                 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
1118                 if (!isVisible && mFillableIds != null) {
1119                     if (mFillableIds.contains(id)) {
1120                         if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1121                         requestHideFillUi(id, view);
1122                     }
1123                 }
1124                 if (mTrackedViews != null) {
1125                     mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
1126                 } else if (sVerbose) {
1127                     Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
1128                 }
1129             } else if (!virtual && isVisible) {
1130                 startAutofillIfNeededLocked(view);
1131             }
1132         }
1133     }
1134 
1135     /**
1136      * Called when a virtual view that supports autofill is entered.
1137      *
1138      * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1139      * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1140      * the absolute bounds could be calculated by:
1141      *
1142      * <pre class="prettyprint">
1143      *   int offset[] = new int[2];
1144      *   getLocationOnScreen(offset);
1145      *   Rect absBounds = new Rect(bounds.left + offset[0],
1146      *       bounds.top + offset[1],
1147      *       bounds.right + offset[0], bounds.bottom + offset[1]);
1148      * </pre>
1149      *
1150      * @param view the virtual view parent.
1151      * @param virtualId id identifying the virtual child inside the parent view.
1152      * @param absBounds absolute boundaries of the virtual view in the screen.
1153      */
notifyViewEntered(@onNull View view, int virtualId, @NonNull Rect absBounds)1154     public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1155         notifyViewEntered(view, virtualId, absBounds, 0);
1156     }
1157 
notifyViewEntered(View view, int virtualId, Rect bounds, int flags)1158     private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
1159         if (!hasAutofillFeature()) {
1160             return;
1161         }
1162         AutofillCallback callback;
1163         synchronized (mLock) {
1164             callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
1165         }
1166 
1167         if (callback != null) {
1168             callback.onAutofillEvent(view, virtualId,
1169                     AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1170         }
1171     }
1172 
1173     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
1174     @GuardedBy("mLock")
notifyViewEnteredLocked(View view, int virtualId, Rect bounds, int flags)1175     private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
1176                                                      int flags) {
1177         final AutofillId id = getAutofillId(view, virtualId);
1178         AutofillCallback callback = null;
1179         if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
1180 
1181         ensureServiceClientAddedIfNeededLocked();
1182 
1183         if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
1184             if (sVerbose) {
1185                 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1186             }
1187             if (mCallback != null) {
1188                 callback = mCallback;
1189             }
1190         } else {
1191             // don't notify entered when Activity is already in background
1192             if (!isClientDisablingEnterExitEvent()) {
1193                 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1194                     flags |= FLAG_PASSWORD_INPUT_TYPE;
1195                 }
1196 
1197                 if (!isActiveLocked()) {
1198                     // Starts new session.
1199                     startSessionLocked(id, bounds, null, flags);
1200                 } else {
1201                     // Update focus on existing session.
1202                     if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1203                         if (sDebug) {
1204                             Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1205                                     + "mForAugmentedAutofillOnly on manual request");
1206                         }
1207                         mForAugmentedAutofillOnly = false;
1208                     }
1209                     updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
1210                 }
1211                 addEnteredIdLocked(id);
1212             }
1213         }
1214         return callback;
1215     }
1216 
1217     @GuardedBy("mLock")
addEnteredIdLocked(@onNull AutofillId id)1218     private void addEnteredIdLocked(@NonNull AutofillId id) {
1219         if (mEnteredIds == null) {
1220             mEnteredIds = new ArraySet<>(1);
1221         }
1222         id.resetSessionId();
1223         mEnteredIds.add(id);
1224     }
1225 
1226     /**
1227      * Called when a virtual view that supports autofill is exited.
1228      *
1229      * @param view the virtual view parent.
1230      * @param virtualId id identifying the virtual child inside the parent view.
1231      */
notifyViewExited(@onNull View view, int virtualId)1232     public void notifyViewExited(@NonNull View view, int virtualId) {
1233         if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
1234         if (!hasAutofillFeature()) {
1235             return;
1236         }
1237         synchronized (mLock) {
1238             notifyViewExitedLocked(view, virtualId);
1239         }
1240     }
1241 
1242     @GuardedBy("mLock")
notifyViewExitedLocked(@onNull View view, int virtualId)1243     private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1244         ensureServiceClientAddedIfNeededLocked();
1245 
1246         if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
1247             // don't notify exited when Activity is already in background
1248             if (!isClientDisablingEnterExitEvent()) {
1249                 final AutofillId id = getAutofillId(view, virtualId);
1250 
1251                 // Update focus on existing session.
1252                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
1253             }
1254         }
1255     }
1256 
1257     /**
1258      * Called to indicate the value of an autofillable {@link View} changed.
1259      *
1260      * @param view view whose value changed.
1261      */
notifyValueChanged(View view)1262     public void notifyValueChanged(View view) {
1263         if (!hasAutofillFeature()) {
1264             return;
1265         }
1266         AutofillId id = null;
1267         boolean valueWasRead = false;
1268         AutofillValue value = null;
1269 
1270         synchronized (mLock) {
1271             // If the session is gone some fields might still be highlighted, hence we have to
1272             // remove the isAutofilled property even if no sessions are active.
1273             if (mLastAutofilledData == null) {
1274                 view.setAutofilled(false, false);
1275             } else {
1276                 id = view.getAutofillId();
1277                 if (mLastAutofilledData.containsKey(id)) {
1278                     value = view.getAutofillValue();
1279                     valueWasRead = true;
1280                     final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1;
1281 
1282                     if (Objects.equals(mLastAutofilledData.get(id), value)) {
1283                         view.setAutofilled(true, hideHighlight);
1284                     } else {
1285                         view.setAutofilled(false, false);
1286                         mLastAutofilledData.remove(id);
1287                     }
1288                 } else {
1289                     view.setAutofilled(false, false);
1290                 }
1291             }
1292 
1293             if (!mEnabled || !isActiveLocked()) {
1294                 if (!startAutofillIfNeededLocked(view)) {
1295                     if (sVerbose) {
1296                         Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1297                                 + "): ignoring on state " + getStateAsStringLocked());
1298                     }
1299                 }
1300                 return;
1301             }
1302 
1303             if (id == null) {
1304                 id = view.getAutofillId();
1305             }
1306 
1307             if (!valueWasRead) {
1308                 value = view.getAutofillValue();
1309             }
1310 
1311             updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
1312         }
1313     }
1314 
1315     /**
1316      * Called to indicate the value of an autofillable virtual view has changed.
1317      *
1318      * @param view the virtual view parent.
1319      * @param virtualId id identifying the virtual child inside the parent view.
1320      * @param value new value of the child.
1321      */
notifyValueChanged(View view, int virtualId, AutofillValue value)1322     public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
1323         if (!hasAutofillFeature()) {
1324             return;
1325         }
1326         synchronized (mLock) {
1327             if (!mEnabled || !isActiveLocked()) {
1328                 if (sVerbose) {
1329                     Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1330                             + "): ignoring on state " + getStateAsStringLocked());
1331                 }
1332                 return;
1333             }
1334 
1335             final AutofillId id = getAutofillId(view, virtualId);
1336             updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
1337         }
1338     }
1339 
1340     /**
1341      * Called to indicate a {@link View} is clicked.
1342      *
1343      * @param view view that has been clicked.
1344      */
notifyViewClicked(@onNull View view)1345     public void notifyViewClicked(@NonNull View view) {
1346         notifyViewClicked(view.getAutofillId());
1347     }
1348 
1349     /**
1350      * Called to indicate a virtual view has been clicked.
1351      *
1352      * @param view the virtual view parent.
1353      * @param virtualId id identifying the virtual child inside the parent view.
1354      */
notifyViewClicked(@onNull View view, int virtualId)1355     public void notifyViewClicked(@NonNull View view, int virtualId) {
1356         notifyViewClicked(getAutofillId(view, virtualId));
1357     }
1358 
notifyViewClicked(AutofillId id)1359     private void notifyViewClicked(AutofillId id) {
1360         if (!hasAutofillFeature()) {
1361             return;
1362         }
1363         if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1364 
1365         synchronized (mLock) {
1366             if (!mEnabled || !isActiveLocked()) {
1367                 return;
1368             }
1369             if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1370                 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1371                 commitLocked();
1372                 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
1373             }
1374         }
1375     }
1376 
1377     /**
1378      * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1379      *
1380      * @hide
1381      */
onActivityFinishing()1382     public void onActivityFinishing() {
1383         if (!hasAutofillFeature()) {
1384             return;
1385         }
1386         synchronized (mLock) {
1387             if (mSaveOnFinish) {
1388                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
1389                 commitLocked();
1390             } else {
1391                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
1392                 cancelLocked();
1393             }
1394         }
1395     }
1396 
1397     /**
1398      * Called to indicate the current autofill context should be commited.
1399      *
1400      * <p>This method is typically called by {@link View Views} that manage virtual views; for
1401      * example, when the view is rendering an {@code HTML} page with a form and virtual views
1402      * that represent the HTML elements, it should call this method after the form is submitted and
1403      * another page is rendered.
1404      *
1405      * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1406      * methods such as {@link android.app.Activity#finish()}.
1407      */
commit()1408     public void commit() {
1409         if (!hasAutofillFeature()) {
1410             return;
1411         }
1412         if (sVerbose) Log.v(TAG, "commit() called by app");
1413         synchronized (mLock) {
1414             commitLocked();
1415         }
1416     }
1417 
1418     @GuardedBy("mLock")
commitLocked()1419     private void commitLocked() {
1420         if (!mEnabled && !isActiveLocked()) {
1421             return;
1422         }
1423         finishSessionLocked();
1424     }
1425 
1426     /**
1427      * Called to indicate the current autofill context should be cancelled.
1428      *
1429      * <p>This method is typically called by {@link View Views} that manage virtual views; for
1430      * example, when the view is rendering an {@code HTML} page with a form and virtual views
1431      * that represent the HTML elements, it should call this method if the user does not post the
1432      * form but moves to another form in this page.
1433      *
1434      * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1435      * methods such as {@link android.app.Activity#finish()}.
1436      */
cancel()1437     public void cancel() {
1438         if (sVerbose) Log.v(TAG, "cancel() called by app or augmented autofill service");
1439         if (!hasAutofillFeature()) {
1440             return;
1441         }
1442         synchronized (mLock) {
1443             cancelLocked();
1444         }
1445     }
1446 
1447     @GuardedBy("mLock")
cancelLocked()1448     private void cancelLocked() {
1449         if (!mEnabled && !isActiveLocked()) {
1450             return;
1451         }
1452         cancelSessionLocked();
1453     }
1454 
1455     /** @hide */
disableOwnedAutofillServices()1456     public void disableOwnedAutofillServices() {
1457         disableAutofillServices();
1458     }
1459 
1460     /**
1461      * If the app calling this API has enabled autofill services they
1462      * will be disabled.
1463      */
disableAutofillServices()1464     public void disableAutofillServices() {
1465         if (!hasAutofillFeature()) {
1466             return;
1467         }
1468         try {
1469             mService.disableOwnedAutofillServices(mContext.getUserId());
1470         } catch (RemoteException e) {
1471             throw e.rethrowFromSystemServer();
1472         }
1473     }
1474 
1475     /**
1476      * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1477      * enabled for the current user, or {@code false} otherwise.
1478      */
hasEnabledAutofillServices()1479     public boolean hasEnabledAutofillServices() {
1480         if (mService == null) return false;
1481 
1482         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1483         try {
1484             mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(),
1485                     receiver);
1486             return receiver.getIntResult() == 1;
1487         } catch (RemoteException e) {
1488             throw e.rethrowFromSystemServer();
1489         } catch (SyncResultReceiver.TimeoutException e) {
1490             throw new RuntimeException("Fail to get enabled autofill services status.");
1491         }
1492     }
1493 
1494     /**
1495      * Returns the component name of the {@link AutofillService} that is enabled for the current
1496      * user.
1497      */
1498     @Nullable
getAutofillServiceComponentName()1499     public ComponentName getAutofillServiceComponentName() {
1500         if (mService == null) return null;
1501 
1502         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1503         try {
1504             mService.getAutofillServiceComponentName(receiver);
1505             return receiver.getParcelableResult();
1506         } catch (RemoteException e) {
1507             throw e.rethrowFromSystemServer();
1508         } catch (SyncResultReceiver.TimeoutException e) {
1509             throw new RuntimeException("Fail to get autofill services component name.");
1510         }
1511     }
1512 
1513     /**
1514      * Gets the id of the {@link UserData} used for
1515      * <a href="AutofillService.html#FieldClassification">field classification</a>.
1516      *
1517      * <p>This method is useful when the service must check the status of the {@link UserData} in
1518      * the device without fetching the whole object.
1519      *
1520      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1521      * and it's ignored if the caller currently doesn't have an enabled autofill service for
1522      * the user.
1523      *
1524      * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1525      * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1526      * service for the user.
1527      */
getUserDataId()1528     @Nullable public String getUserDataId() {
1529         try {
1530             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1531             mService.getUserDataId(receiver);
1532             return receiver.getStringResult();
1533         } catch (RemoteException e) {
1534             throw e.rethrowFromSystemServer();
1535         } catch (SyncResultReceiver.TimeoutException e) {
1536             throw new RuntimeException("Fail to get user data id for field classification.");
1537         }
1538     }
1539 
1540     /**
1541      * Gets the user data used for
1542      * <a href="AutofillService.html#FieldClassification">field classification</a>.
1543      *
1544      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1545      * and it's ignored if the caller currently doesn't have an enabled autofill service for
1546      * the user.
1547      *
1548      * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1549      * reset or if the caller currently does not have an enabled autofill service for the user.
1550      */
getUserData()1551     @Nullable public UserData getUserData() {
1552         try {
1553             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1554             mService.getUserData(receiver);
1555             return receiver.getParcelableResult();
1556         } catch (RemoteException e) {
1557             throw e.rethrowFromSystemServer();
1558         } catch (SyncResultReceiver.TimeoutException e) {
1559             throw new RuntimeException("Fail to get user data for field classification.");
1560         }
1561     }
1562 
1563     /**
1564      * Sets the {@link UserData} used for
1565      * <a href="AutofillService.html#FieldClassification">field classification</a>
1566      *
1567      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1568      * and it's ignored if the caller currently doesn't have an enabled autofill service for
1569      * the user.
1570      */
setUserData(@ullable UserData userData)1571     public void setUserData(@Nullable UserData userData) {
1572         try {
1573             mService.setUserData(userData);
1574         } catch (RemoteException e) {
1575             throw e.rethrowFromSystemServer();
1576         }
1577     }
1578 
1579     /**
1580      * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1581      * enabled.
1582      *
1583      * <p>As field classification is an expensive operation, it could be disabled, either
1584      * temporarily (for example, because the service exceeded a rate-limit threshold) or
1585      * permanently (for example, because the device is a low-level device).
1586      *
1587      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1588      * and it's ignored if the caller currently doesn't have an enabled autofill service for
1589      * the user.
1590      */
isFieldClassificationEnabled()1591     public boolean isFieldClassificationEnabled() {
1592         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1593         try {
1594             mService.isFieldClassificationEnabled(receiver);
1595             return receiver.getIntResult() == 1;
1596         } catch (RemoteException e) {
1597             throw e.rethrowFromSystemServer();
1598         } catch (SyncResultReceiver.TimeoutException e) {
1599             throw new RuntimeException("Fail to get field classification enabled status.");
1600         }
1601     }
1602 
1603     /**
1604      * Gets the name of the default algorithm used for
1605      * <a href="AutofillService.html#FieldClassification">field classification</a>.
1606      *
1607      * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1608      * set.
1609      *
1610      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1611      * and it's ignored if the caller currently doesn't have an enabled autofill service for
1612      * the user.
1613      */
1614     @Nullable
getDefaultFieldClassificationAlgorithm()1615     public String getDefaultFieldClassificationAlgorithm() {
1616         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1617         try {
1618             mService.getDefaultFieldClassificationAlgorithm(receiver);
1619             return receiver.getStringResult();
1620         } catch (RemoteException e) {
1621             throw e.rethrowFromSystemServer();
1622         } catch (SyncResultReceiver.TimeoutException e) {
1623             throw new RuntimeException("Fail to get default field classification algorithm.");
1624         }
1625     }
1626 
1627     /**
1628      * Gets the name of all algorithms currently available for
1629      * <a href="AutofillService.html#FieldClassification">field classification</a>.
1630      *
1631      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1632      * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1633      * for the user.
1634      */
1635     @NonNull
getAvailableFieldClassificationAlgorithms()1636     public List<String> getAvailableFieldClassificationAlgorithms() {
1637         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1638         try {
1639             mService.getAvailableFieldClassificationAlgorithms(receiver);
1640             final String[] algorithms = receiver.getStringArrayResult();
1641             return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
1642         } catch (RemoteException e) {
1643             throw e.rethrowFromSystemServer();
1644         } catch (SyncResultReceiver.TimeoutException e) {
1645             throw new RuntimeException("Fail to get available field classification algorithms.");
1646         }
1647     }
1648 
1649     /**
1650      * Returns {@code true} if autofill is supported by the current device and
1651      * is supported for this user.
1652      *
1653      * <p>Autofill is typically supported, but it could be unsupported in cases like:
1654      * <ol>
1655      *     <li>Low-end devices.
1656      *     <li>Device policy rules that forbid its usage.
1657      * </ol>
1658      */
isAutofillSupported()1659     public boolean isAutofillSupported() {
1660         if (mService == null) return false;
1661 
1662         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1663         try {
1664             mService.isServiceSupported(mContext.getUserId(), receiver);
1665             return receiver.getIntResult() == 1;
1666         } catch (RemoteException e) {
1667             throw e.rethrowFromSystemServer();
1668         } catch (SyncResultReceiver.TimeoutException e) {
1669             throw new RuntimeException("Fail to get autofill supported status.");
1670         }
1671     }
1672 
1673     // Note: don't need to use locked suffix because mContext is final.
getClient()1674     private AutofillClient getClient() {
1675         final AutofillClient client = mContext.getAutofillClient();
1676         if (client == null && sVerbose) {
1677             Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1678                     + mContext);
1679         }
1680         return client;
1681     }
1682 
1683     /**
1684      * Check if autofill ui is showing, must be called on UI thread.
1685      * @hide
1686      */
isAutofillUiShowing()1687     public boolean isAutofillUiShowing() {
1688         final AutofillClient client = mContext.getAutofillClient();
1689         return client != null && client.autofillClientIsFillUiShowing();
1690     }
1691 
1692     /** @hide */
onAuthenticationResult(int authenticationId, Intent data, View focusView)1693     public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
1694         if (!hasAutofillFeature()) {
1695             return;
1696         }
1697         // TODO: the result code is being ignored, so this method is not reliably
1698         // handling the cases where it's not RESULT_OK: it works fine if the service does not
1699         // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1700         // service set the extra and returned RESULT_CANCELED...
1701 
1702         if (sDebug) {
1703             Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
1704         }
1705 
1706         synchronized (mLock) {
1707             if (!isActiveLocked()) {
1708                 return;
1709             }
1710             // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1711             // of app activity.  We enforce enter event to re-show fill ui in such case.
1712             // CTS example:
1713             //     LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1714             //     LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1715             if (!mOnInvisibleCalled && focusView != null
1716                     && focusView.canNotifyAutofillEnterExitEvent()) {
1717                 notifyViewExitedLocked(focusView);
1718                 notifyViewEnteredLocked(focusView, 0);
1719             }
1720             if (data == null) {
1721                 // data is set to null when result is not RESULT_OK
1722                 return;
1723             }
1724 
1725             final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1726             final Bundle responseData = new Bundle();
1727             responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
1728             final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1729             if (newClientState != null) {
1730                 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1731             }
1732             try {
1733                 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1734                         mContext.getUserId());
1735             } catch (RemoteException e) {
1736                 Log.e(TAG, "Error delivering authentication result", e);
1737             }
1738         }
1739     }
1740 
1741     /**
1742      * Gets the next unique autofill ID for the activity context.
1743      *
1744      * <p>Typically used to manage views whose content is recycled - see
1745      * {@link View#setAutofillId(AutofillId)} for more info.
1746      *
1747      * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1748      * the {@link Context} associated with this {@link AutofillManager}.
1749      */
1750     @Nullable
getNextAutofillId()1751     public AutofillId getNextAutofillId() {
1752         final AutofillClient client = getClient();
1753         if (client == null) return null;
1754 
1755         final AutofillId id = client.autofillClientGetNextAutofillId();
1756 
1757         if (id == null && sDebug) {
1758             Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1759         }
1760 
1761         return id;
1762     }
1763 
getAutofillId(View parent, int virtualId)1764     private static AutofillId getAutofillId(View parent, int virtualId) {
1765         return new AutofillId(parent.getAutofillViewId(), virtualId);
1766     }
1767 
1768     @GuardedBy("mLock")
startSessionLocked(@onNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags)1769     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1770             @NonNull AutofillValue value, int flags) {
1771         if (mEnteredForAugmentedAutofillIds != null
1772                 && mEnteredForAugmentedAutofillIds.contains(id)
1773                 || mEnabledForAugmentedAutofillOnly) {
1774             if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
1775             flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
1776         }
1777         if (sVerbose) {
1778             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
1779                     + ", flags=" + flags + ", state=" + getStateAsStringLocked()
1780                     + ", compatMode=" + isCompatibilityModeEnabledLocked()
1781                     + ", augmentedOnly=" + mForAugmentedAutofillOnly
1782                     + ", enabledAugmentedOnly=" + mEnabledForAugmentedAutofillOnly
1783                     + ", enteredIds=" + mEnteredIds);
1784         }
1785         // We need to reset the augmented-only state when a manual request is made, as it's possible
1786         // that the service returned null for the first request and now the user is manually
1787         // requesting autofill to trigger a custom UI provided by the service.
1788         if (mForAugmentedAutofillOnly && !mEnabledForAugmentedAutofillOnly
1789                 && (flags & FLAG_MANUAL_REQUEST) != 0) {
1790             if (sVerbose) {
1791                 Log.v(TAG, "resetting mForAugmentedAutofillOnly on manual autofill request");
1792             }
1793             mForAugmentedAutofillOnly = false;
1794         }
1795         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
1796             if (sVerbose) {
1797                 Log.v(TAG, "not automatically starting session for " + id
1798                         + " on state " + getStateAsStringLocked() + " and flags " + flags);
1799             }
1800             return;
1801         }
1802         try {
1803             final AutofillClient client = getClient();
1804             if (client == null) return; // NOTE: getClient() already logged it..
1805 
1806             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1807             final ComponentName componentName = client.autofillClientGetComponentName();
1808 
1809             if (!mEnabledForAugmentedAutofillOnly && mOptions != null
1810                     && mOptions.isAutofillDisabledLocked(componentName)) {
1811                 if (mOptions.isAugmentedAutofillEnabled(mContext)) {
1812                     if (sDebug) {
1813                         Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
1814                                 + "whitelisted for augmented autofill");
1815                         flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
1816                     }
1817                 } else {
1818                     if (sDebug) {
1819                         Log.d(TAG, "startSession(" + componentName + "): ignored because "
1820                                 + "disabled by service and not whitelisted for augmented autofill");
1821                     }
1822                     setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
1823                     client.autofillClientResetableStateAvailable();
1824                     return;
1825                 }
1826             }
1827 
1828             mService.startSession(client.autofillClientGetActivityToken(),
1829                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1830                     mCallback != null, flags, componentName,
1831                     isCompatibilityModeEnabledLocked(), receiver);
1832             mSessionId = receiver.getIntResult();
1833             if (mSessionId != NO_SESSION) {
1834                 mState = STATE_ACTIVE;
1835             }
1836             final int extraFlags = receiver.getOptionalExtraIntResult(0);
1837             if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
1838                 if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
1839                 mForAugmentedAutofillOnly = true;
1840             }
1841             client.autofillClientResetableStateAvailable();
1842         } catch (RemoteException e) {
1843             throw e.rethrowFromSystemServer();
1844         } catch (SyncResultReceiver.TimeoutException e) {
1845             // no-op, just log the error message.
1846             Log.w(TAG, "Exception getting result from SyncResultReceiver: " + e);
1847         }
1848     }
1849 
1850     @GuardedBy("mLock")
finishSessionLocked()1851     private void finishSessionLocked() {
1852         if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
1853 
1854         if (!isActiveLocked()) return;
1855 
1856         try {
1857             mService.finishSession(mSessionId, mContext.getUserId());
1858         } catch (RemoteException e) {
1859             throw e.rethrowFromSystemServer();
1860         }
1861 
1862         resetSessionLocked(/* resetEnteredIds= */ true);
1863     }
1864 
1865     @GuardedBy("mLock")
cancelSessionLocked()1866     private void cancelSessionLocked() {
1867         if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
1868 
1869         if (!isActiveLocked()) return;
1870 
1871         try {
1872             mService.cancelSession(mSessionId, mContext.getUserId());
1873         } catch (RemoteException e) {
1874             throw e.rethrowFromSystemServer();
1875         }
1876 
1877         resetSessionLocked(/* resetEnteredIds= */ true);
1878     }
1879 
1880     @GuardedBy("mLock")
resetSessionLocked(boolean resetEnteredIds)1881     private void resetSessionLocked(boolean resetEnteredIds) {
1882         mSessionId = NO_SESSION;
1883         mState = STATE_UNKNOWN;
1884         mTrackedViews = null;
1885         mFillableIds = null;
1886         mSaveTriggerId = null;
1887         mIdShownFillUi = null;
1888         if (resetEnteredIds) {
1889             mEnteredIds = null;
1890         }
1891     }
1892 
1893     @GuardedBy("mLock")
updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, int flags)1894     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1895             int flags) {
1896         if (sVerbose) {
1897             Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1898                     + ", value=" + value + ", action=" + action + ", flags=" + flags);
1899         }
1900         try {
1901             mService.updateSession(mSessionId, id, bounds, value, action, flags,
1902                     mContext.getUserId());
1903         } catch (RemoteException e) {
1904             throw e.rethrowFromSystemServer();
1905         }
1906     }
1907 
1908     @GuardedBy("mLock")
ensureServiceClientAddedIfNeededLocked()1909     private void ensureServiceClientAddedIfNeededLocked() {
1910         final AutofillClient client = getClient();
1911         if (client == null) {
1912             return;
1913         }
1914 
1915         if (mServiceClient == null) {
1916             mServiceClient = new AutofillManagerClient(this);
1917             try {
1918                 final int userId = mContext.getUserId();
1919                 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1920                 mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
1921                         userId, receiver);
1922                 int flags = 0;
1923                 try {
1924                     flags = receiver.getIntResult();
1925                 } catch (SyncResultReceiver.TimeoutException e) {
1926                     Log.w(TAG, "Failed to initialize autofill: " + e);
1927                     return;
1928                 }
1929                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1930                 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1931                 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
1932                 mEnabledForAugmentedAutofillOnly = (flags
1933                         & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
1934                 if (sVerbose) {
1935                     Log.v(TAG, "receiver results: flags=" + flags + " enabled=" + mEnabled
1936                             + ", enabledForAugmentedOnly: " + mEnabledForAugmentedAutofillOnly);
1937                 }
1938                 final IAutoFillManager service = mService;
1939                 final IAutoFillManagerClient serviceClient = mServiceClient;
1940                 mServiceClientCleaner = Cleaner.create(this, () -> {
1941                     // TODO(b/123100811): call service to also remove reference to
1942                     // mAugmentedAutofillServiceClient
1943                     try {
1944                         service.removeClient(serviceClient, userId);
1945                     } catch (RemoteException e) {
1946                     }
1947                 });
1948             } catch (RemoteException e) {
1949                 throw e.rethrowFromSystemServer();
1950             }
1951         }
1952     }
1953 
1954     @GuardedBy("mLock")
startAutofillIfNeededLocked(View view)1955     private boolean startAutofillIfNeededLocked(View view) {
1956         if (mState == STATE_UNKNOWN
1957                 && mSessionId == NO_SESSION
1958                 && view instanceof EditText
1959                 && !TextUtils.isEmpty(((EditText) view).getText())
1960                 && !view.isFocused()
1961                 && view.isImportantForAutofill()
1962                 && view.isLaidOut()
1963                 && view.isVisibleToUser()) {
1964 
1965             ensureServiceClientAddedIfNeededLocked();
1966 
1967             if (sVerbose) {
1968                 Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
1969             }
1970             if (mEnabled && !isClientDisablingEnterExitEvent()) {
1971                 final AutofillId id = view.getAutofillId();
1972                 final AutofillValue value = view.getAutofillValue();
1973                 // Starts new session.
1974                 startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
1975                 // Updates value.
1976                 updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
1977                         /* flags= */ 0);
1978                 addEnteredIdLocked(id);
1979                 return true;
1980             }
1981         }
1982         return false;
1983     }
1984 
1985     /**
1986      * Registers a {@link AutofillCallback} to receive autofill events.
1987      *
1988      * @param callback callback to receive events.
1989      */
registerCallback(@ullable AutofillCallback callback)1990     public void registerCallback(@Nullable AutofillCallback callback) {
1991         if (!hasAutofillFeature()) {
1992             return;
1993         }
1994         synchronized (mLock) {
1995             if (callback == null) return;
1996 
1997             final boolean hadCallback = mCallback != null;
1998             mCallback = callback;
1999 
2000             if (!hadCallback) {
2001                 try {
2002                     mService.setHasCallback(mSessionId, mContext.getUserId(), true);
2003                 } catch (RemoteException e) {
2004                     throw e.rethrowFromSystemServer();
2005                 }
2006             }
2007         }
2008     }
2009 
2010     /**
2011      * Unregisters a {@link AutofillCallback} to receive autofill events.
2012      *
2013      * @param callback callback to stop receiving events.
2014      */
unregisterCallback(@ullable AutofillCallback callback)2015     public void unregisterCallback(@Nullable AutofillCallback callback) {
2016         if (!hasAutofillFeature()) {
2017             return;
2018         }
2019         synchronized (mLock) {
2020             if (callback == null || mCallback == null || callback != mCallback) return;
2021 
2022             mCallback = null;
2023 
2024             try {
2025                 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
2026             } catch (RemoteException e) {
2027                 throw e.rethrowFromSystemServer();
2028             }
2029         }
2030     }
2031 
2032     /**
2033      * Explicitly limits augmented autofill to the given packages and activities.
2034      *
2035      * <p>To reset the whitelist, call it passing {@code null} to both arguments.
2036      *
2037      * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
2038      * apps that uses addresses. For example, if the service wants to support augmented autofill on
2039      * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
2040      * of {@code AddressApp2}, it would call:
2041      * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
2042      * Arrays.asList(new ComponentName("AddressApp2", "act1"),
2043      * new ComponentName("AddressApp2", "act2")));}
2044      *
2045      * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
2046      * service, and it's ignored if the caller isn't it.
2047      *
2048      * @hide
2049      */
2050     @SystemApi
2051     @TestApi
setAugmentedAutofillWhitelist(@ullable Set<String> packages, @Nullable Set<ComponentName> activities)2052     public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
2053             @Nullable Set<ComponentName> activities) {
2054         if (!hasAutofillFeature()) {
2055             return;
2056         }
2057 
2058         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2059         int resultCode;
2060         try {
2061             mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
2062                     resultReceiver);
2063             resultCode = resultReceiver.getIntResult();
2064         } catch (RemoteException e) {
2065             throw e.rethrowFromSystemServer();
2066         } catch (SyncResultReceiver.TimeoutException e) {
2067             Log.e(TAG, "Fail to get the result of set AugmentedAutofill whitelist. " + e);
2068             return;
2069         }
2070         switch (resultCode) {
2071             case RESULT_OK:
2072                 return;
2073             case RESULT_CODE_NOT_SERVICE:
2074                 throw new SecurityException("caller is not user's Augmented Autofill Service");
2075             default:
2076                 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
2077                         + resultCode);
2078         }
2079     }
2080 
2081     /**
2082      * Notifies that a non-autofillable view was entered because the activity is whitelisted for
2083      * augmented autofill.
2084      *
2085      * <p>This method is necessary to set the right flag on start, so the server-side session
2086      * doesn't trigger the standard autofill workflow, but the augmented's instead.
2087      *
2088      * @hide
2089      */
notifyViewEnteredForAugmentedAutofill(@onNull View view)2090     public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
2091         final AutofillId id = view.getAutofillId();
2092         synchronized (mLock) {
2093             if (mEnteredForAugmentedAutofillIds == null) {
2094                 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
2095             }
2096             mEnteredForAugmentedAutofillIds.add(id);
2097         }
2098     }
2099 
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)2100     private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2101             Rect anchorBounds, IAutofillWindowPresenter presenter) {
2102         final View anchor = findView(id);
2103         if (anchor == null) {
2104             return;
2105         }
2106 
2107         AutofillCallback callback = null;
2108         synchronized (mLock) {
2109             if (mSessionId == sessionId) {
2110                 AutofillClient client = getClient();
2111 
2112                 if (client != null) {
2113                     if (client.autofillClientRequestShowFillUi(anchor, width, height,
2114                             anchorBounds, presenter)) {
2115                         callback = mCallback;
2116                         mIdShownFillUi = id;
2117                     }
2118                 }
2119             }
2120         }
2121 
2122         if (callback != null) {
2123             if (id.isVirtualInt()) {
2124                 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
2125                         AutofillCallback.EVENT_INPUT_SHOWN);
2126             } else {
2127                 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
2128             }
2129         }
2130     }
2131 
authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)2132     private void authenticate(int sessionId, int authenticationId, IntentSender intent,
2133             Intent fillInIntent, boolean authenticateInline) {
2134         synchronized (mLock) {
2135             if (sessionId == mSessionId) {
2136                 final AutofillClient client = getClient();
2137                 if (client != null) {
2138                     // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
2139                     // before onAuthenticationResult()
2140                     mOnInvisibleCalled = false;
2141                     client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
2142                             authenticateInline);
2143                 }
2144             }
2145         }
2146     }
2147 
dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent)2148     private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
2149         final View anchor = findView(id);
2150         if (anchor == null) {
2151             return;
2152         }
2153 
2154         synchronized (mLock) {
2155             if (mSessionId == sessionId) {
2156                 AutofillClient client = getClient();
2157 
2158                 if (client != null) {
2159                     client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
2160                 }
2161             }
2162         }
2163     }
2164 
2165     /** @hide */
2166     public static final int SET_STATE_FLAG_ENABLED = 0x01;
2167     /** @hide */
2168     public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
2169     /** @hide */
2170     public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
2171     /** @hide */
2172     public static final int SET_STATE_FLAG_DEBUG = 0x08;
2173     /** @hide */
2174     public static final int SET_STATE_FLAG_VERBOSE = 0x10;
2175     /** @hide */
2176     public static final int SET_STATE_FLAG_FOR_AUTOFILL_ONLY = 0x20;
2177 
setState(int flags)2178     private void setState(int flags) {
2179         if (sVerbose) {
2180             Log.v(TAG, "setState(" + flags + ": " + DebugUtils.flagsToString(AutofillManager.class,
2181                     "SET_STATE_FLAG_", flags) + ")");
2182         }
2183         synchronized (mLock) {
2184             if ((flags & SET_STATE_FLAG_FOR_AUTOFILL_ONLY) != 0) {
2185                 mForAugmentedAutofillOnly = true;
2186                 // NOTE: returning right away as this is the only flag set, at least currently...
2187                 return;
2188             }
2189             mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
2190             if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
2191                 // Reset the session state
2192                 resetSessionLocked(/* resetEnteredIds= */ true);
2193             }
2194             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
2195                 // Reset connection to system
2196                 mServiceClient = null;
2197                 mAugmentedAutofillServiceClient = null;
2198                 if (mServiceClientCleaner != null) {
2199                     mServiceClientCleaner.clean();
2200                     mServiceClientCleaner = null;
2201                 }
2202                 notifyReenableAutofill();
2203             }
2204         }
2205         sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2206         sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
2207     }
2208 
2209     /**
2210      * Sets a view as autofilled if the current value is the {code targetValue}.
2211      *
2212      * @param view The view that is to be autofilled
2213      * @param targetValue The value we want to fill into view
2214      */
setAutofilledIfValuesIs(@onNull View view, @Nullable AutofillValue targetValue, boolean hideHighlight)2215     private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
2216             boolean hideHighlight) {
2217         AutofillValue currentValue = view.getAutofillValue();
2218         if (Objects.equals(currentValue, targetValue)) {
2219             synchronized (mLock) {
2220                 if (mLastAutofilledData == null) {
2221                     mLastAutofilledData = new ParcelableMap(1);
2222                 }
2223                 mLastAutofilledData.put(view.getAutofillId(), targetValue);
2224             }
2225             view.setAutofilled(true, hideHighlight);
2226         }
2227     }
2228 
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)2229     private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
2230             boolean hideHighlight) {
2231         synchronized (mLock) {
2232             if (sessionId != mSessionId) {
2233                 return;
2234             }
2235 
2236             final AutofillClient client = getClient();
2237             if (client == null) {
2238                 return;
2239             }
2240 
2241             final int itemCount = ids.size();
2242             int numApplied = 0;
2243             ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
2244             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
2245                     Helper.toArray(ids));
2246 
2247             ArrayList<AutofillId> failedIds = null;
2248 
2249             for (int i = 0; i < itemCount; i++) {
2250                 final AutofillId id = ids.get(i);
2251                 final AutofillValue value = values.get(i);
2252                 final View view = views[i];
2253                 if (view == null) {
2254                     // Most likely view has been removed after the initial request was sent to the
2255                     // the service; this is fine, but we need to update the view status in the
2256                     // server side so it can be triggered again.
2257                     Log.d(TAG, "autofill(): no View with id " + id);
2258                     if (failedIds == null) {
2259                         failedIds = new ArrayList<>();
2260                     }
2261                     failedIds.add(id);
2262                     continue;
2263                 }
2264                 if (id.isVirtualInt()) {
2265                     if (virtualValues == null) {
2266                         // Most likely there will be just one view with virtual children.
2267                         virtualValues = new ArrayMap<>(1);
2268                     }
2269                     SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
2270                     if (valuesByParent == null) {
2271                         // We don't know the size yet, but usually it will be just a few fields...
2272                         valuesByParent = new SparseArray<>(5);
2273                         virtualValues.put(view, valuesByParent);
2274                     }
2275                     valuesByParent.put(id.getVirtualChildIntId(), value);
2276                 } else {
2277                     // Mark the view as to be autofilled with 'value'
2278                     if (mLastAutofilledData == null) {
2279                         mLastAutofilledData = new ParcelableMap(itemCount - i);
2280                     }
2281                     mLastAutofilledData.put(id, value);
2282 
2283                     view.autofill(value);
2284 
2285                     // Set as autofilled if the values match now, e.g. when the value was updated
2286                     // synchronously.
2287                     // If autofill happens async, the view is set to autofilled in
2288                     // notifyValueChanged.
2289                     setAutofilledIfValuesIs(view, value, hideHighlight);
2290 
2291                     numApplied++;
2292                 }
2293             }
2294 
2295             if (failedIds != null) {
2296                 if (sVerbose) {
2297                     Log.v(TAG, "autofill(): total failed views: " + failedIds);
2298                 }
2299                 try {
2300                     mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
2301                 } catch (RemoteException e) {
2302                     // In theory, we could ignore this error since it's not a big deal, but
2303                     // in reality, we rather crash the app anyways, as the failure could be
2304                     // a consequence of something going wrong on the server side...
2305                     throw e.rethrowFromSystemServer();
2306                 }
2307             }
2308 
2309             if (virtualValues != null) {
2310                 for (int i = 0; i < virtualValues.size(); i++) {
2311                     final View parent = virtualValues.keyAt(i);
2312                     final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
2313                     parent.autofill(childrenValues);
2314                     numApplied += childrenValues.size();
2315                     // TODO: we should provide a callback so the parent can call failures; something
2316                     // like notifyAutofillFailed(View view, int[] childrenIds);
2317                 }
2318             }
2319 
2320             mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
2321                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
2322                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
2323         }
2324     }
2325 
newLog(int category)2326     private LogMaker newLog(int category) {
2327         final LogMaker log = new LogMaker(category)
2328                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
2329 
2330         if (isCompatibilityModeEnabledLocked()) {
2331             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
2332         }
2333         final AutofillClient client = getClient();
2334         if (client == null) {
2335             // Client should never be null here, but it doesn't hurt to check...
2336             log.setPackageName(mContext.getPackageName());
2337         } else {
2338             log.setComponentName(client.autofillClientGetComponentName());
2339         }
2340         return log;
2341     }
2342 
2343     /**
2344      *  Set the tracked views.
2345      *
2346      * @param trackedIds The views to be tracked.
2347      * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
2348      * @param saveOnFinish Finish the session once the activity is finished.
2349      * @param fillableIds Views that might anchor FillUI.
2350      * @param saveTriggerId View that when clicked triggers commit().
2351      */
setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, boolean saveOnAllViewsInvisible, boolean saveOnFinish, @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId)2352     private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
2353             boolean saveOnAllViewsInvisible, boolean saveOnFinish,
2354             @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
2355         if (saveTriggerId != null) {
2356             saveTriggerId.resetSessionId();
2357         }
2358         synchronized (mLock) {
2359             if (sVerbose) {
2360                 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
2361                         + ", trackedIds=" + Arrays.toString(trackedIds)
2362                         + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible
2363                         + ", saveOnFinish=" + saveOnFinish
2364                         + ", fillableIds=" + Arrays.toString(fillableIds)
2365                         + ", saveTrigerId=" + saveTriggerId
2366                         + ", mFillableIds=" + mFillableIds
2367                         + ", mEnabled=" + mEnabled
2368                         + ", mSessionId=" + mSessionId);
2369 
2370             }
2371             if (mEnabled && mSessionId == sessionId) {
2372                 if (saveOnAllViewsInvisible) {
2373                     mTrackedViews = new TrackedViews(trackedIds);
2374                 } else {
2375                     mTrackedViews = null;
2376                 }
2377                 mSaveOnFinish = saveOnFinish;
2378                 if (fillableIds != null) {
2379                     if (mFillableIds == null) {
2380                         mFillableIds = new ArraySet<>(fillableIds.length);
2381                     }
2382                     for (AutofillId id : fillableIds) {
2383                         id.resetSessionId();
2384                         mFillableIds.add(id);
2385                     }
2386                 }
2387 
2388                 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
2389                     // Turn off trigger on previous view id.
2390                     setNotifyOnClickLocked(mSaveTriggerId, false);
2391                 }
2392 
2393                 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
2394                     // Turn on trigger on new view id.
2395                     mSaveTriggerId = saveTriggerId;
2396                     setNotifyOnClickLocked(mSaveTriggerId, true);
2397                 }
2398             }
2399         }
2400     }
2401 
setNotifyOnClickLocked(@onNull AutofillId id, boolean notify)2402     private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
2403         final View view = findView(id);
2404         if (view == null) {
2405             Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
2406             return;
2407         }
2408         view.setNotifyAutofillManagerOnClick(notify);
2409     }
2410 
setSaveUiState(int sessionId, boolean shown)2411     private void setSaveUiState(int sessionId, boolean shown) {
2412         if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
2413         synchronized (mLock) {
2414             if (mSessionId != NO_SESSION) {
2415                 // Race condition: app triggered a new session after the previous session was
2416                 // finished but before server called setSaveUiState() - need to cancel the new
2417                 // session to avoid further inconsistent behavior.
2418                 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
2419                         + ") called on existing session " + mSessionId + "; cancelling it");
2420                 cancelSessionLocked();
2421             }
2422             if (shown) {
2423                 mSessionId = sessionId;
2424                 mState = STATE_SHOWING_SAVE_UI;
2425             } else {
2426                 mSessionId = NO_SESSION;
2427                 mState = STATE_UNKNOWN;
2428             }
2429         }
2430     }
2431 
2432     /**
2433      * Marks the state of the session as finished.
2434      *
2435      * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
2436      *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2437      *  {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
2438      *  changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
2439      *  when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
2440      *  (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
2441      *  service disabled further autofill requests for the activity).
2442      * @param autofillableIds list of ids that could trigger autofill, use to not handle a new
2443      *  session when they're entered.
2444      */
setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds)2445     private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
2446         if (autofillableIds != null) {
2447             for (int i = 0; i < autofillableIds.size(); i++) {
2448                 autofillableIds.get(i).resetSessionId();
2449             }
2450         }
2451         synchronized (mLock) {
2452             if (sVerbose) {
2453                 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
2454                         + getStateAsString(newState) + "; autofillableIds=" + autofillableIds);
2455             }
2456             if (autofillableIds != null) {
2457                 mEnteredIds = new ArraySet<>(autofillableIds);
2458             }
2459             if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
2460                 resetSessionLocked(/* resetEnteredIds= */ true);
2461                 mState = STATE_UNKNOWN;
2462             } else {
2463                 resetSessionLocked(/* resetEnteredIds= */ false);
2464                 mState = newState;
2465             }
2466         }
2467     }
2468 
2469     /**
2470      * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
2471      *
2472      * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
2473      * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
2474      */
getAugmentedAutofillClient(@onNull IResultReceiver result)2475     private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
2476         synchronized (mLock) {
2477             if (mAugmentedAutofillServiceClient == null) {
2478                 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
2479             }
2480             final Bundle resultData = new Bundle();
2481             resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
2482                     mAugmentedAutofillServiceClient.asBinder());
2483 
2484             try {
2485                 result.send(0, resultData);
2486             } catch (RemoteException e) {
2487                 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
2488             }
2489         }
2490     }
2491 
requestShowSoftInput(@onNull AutofillId id)2492     private void requestShowSoftInput(@NonNull AutofillId id) {
2493         if (sVerbose) Log.v(TAG, "requestShowSoftInput(" + id + ")");
2494         final AutofillClient client = getClient();
2495         if (client == null) {
2496             return;
2497         }
2498         final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
2499         if (view == null) {
2500             if (sVerbose) Log.v(TAG, "View is not found");
2501             return;
2502         }
2503         final Handler handler = view.getHandler();
2504         if (handler == null) {
2505             if (sVerbose) Log.v(TAG, "Ignoring requestShowSoftInput due to no handler in view");
2506             return;
2507         }
2508         if (handler.getLooper() != Looper.myLooper()) {
2509             // The view is running on a different thread than our own, so we need to reschedule
2510             // our work for over there.
2511             if (sVerbose) Log.v(TAG, "Scheduling showSoftInput() on the view UI thread");
2512             handler.post(() -> requestShowSoftInputInViewThread(view));
2513         } else {
2514             requestShowSoftInputInViewThread(view);
2515         }
2516     }
2517 
2518     // This method must be called from within the View thread.
requestShowSoftInputInViewThread(@onNull View view)2519     private static void requestShowSoftInputInViewThread(@NonNull View view) {
2520         if (!view.isFocused()) {
2521             Log.w(TAG, "Ignoring requestShowSoftInput() due to non-focused view");
2522             return;
2523         }
2524         final InputMethodManager inputMethodManager = view.getContext().getSystemService(
2525                 InputMethodManager.class);
2526         boolean ret = inputMethodManager.showSoftInput(view, /*flags=*/ 0);
2527         if (sVerbose) Log.v(TAG, " InputMethodManager.showSoftInput returns " + ret);
2528     }
2529 
2530     /** @hide */
requestHideFillUi()2531     public void requestHideFillUi() {
2532         requestHideFillUi(mIdShownFillUi, true);
2533     }
2534 
requestHideFillUi(AutofillId id, boolean force)2535     private void requestHideFillUi(AutofillId id, boolean force) {
2536         final View anchor = id == null ? null : findView(id);
2537         if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
2538         if (anchor == null) {
2539             if (force) {
2540                 // When user taps outside autofill window, force to close fill ui even id does
2541                 // not match.
2542                 AutofillClient client = getClient();
2543                 if (client != null) {
2544                     client.autofillClientRequestHideFillUi();
2545                 }
2546             }
2547             return;
2548         }
2549         requestHideFillUi(id, anchor);
2550     }
2551 
requestHideFillUi(AutofillId id, View anchor)2552     private void requestHideFillUi(AutofillId id, View anchor) {
2553 
2554         AutofillCallback callback = null;
2555         synchronized (mLock) {
2556             // We do not check the session id for two reasons:
2557             // 1. If local and remote session id are off sync the UI would be stuck shown
2558             // 2. There is a race between the user state being destroyed due the fill
2559             //    service being uninstalled and the UI being dismissed.
2560             AutofillClient client = getClient();
2561             if (client != null) {
2562                 if (client.autofillClientRequestHideFillUi()) {
2563                     mIdShownFillUi = null;
2564                     callback = mCallback;
2565                 }
2566             }
2567         }
2568 
2569         if (callback != null) {
2570             if (id.isVirtualInt()) {
2571                 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
2572                         AutofillCallback.EVENT_INPUT_HIDDEN);
2573             } else {
2574                 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
2575             }
2576         }
2577     }
2578 
notifyDisableAutofill(long disableDuration, ComponentName componentName)2579     private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
2580         synchronized (mLock) {
2581             if (mOptions == null) {
2582                 return;
2583             }
2584             long expiration = SystemClock.elapsedRealtime() + disableDuration;
2585             // Protect it against overflow
2586             if (expiration < 0) {
2587                 expiration = Long.MAX_VALUE;
2588             }
2589             if (componentName != null) {
2590                 if (mOptions.disabledActivities == null) {
2591                     mOptions.disabledActivities = new ArrayMap<>();
2592                 }
2593                 mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
2594             } else {
2595                 mOptions.appDisabledExpiration = expiration;
2596             }
2597         }
2598     }
2599 
notifyReenableAutofill()2600     void notifyReenableAutofill() {
2601         synchronized (mLock) {
2602             if (mOptions == null) {
2603                 return;
2604             }
2605             mOptions.appDisabledExpiration = 0;
2606             mOptions.disabledActivities = null;
2607         }
2608     }
2609 
notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState)2610     private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
2611         if (sVerbose) {
2612             Log.v(TAG, "notifyNoFillUi(): sessionFinishedState=" + sessionFinishedState);
2613         }
2614         final View anchor = findView(id);
2615         if (anchor == null) {
2616             return;
2617         }
2618 
2619         notifyCallback(sessionId, id, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2620 
2621         if (sessionFinishedState != STATE_UNKNOWN) {
2622             // Callback call was "hijacked" to also update the session state.
2623             setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
2624         }
2625     }
2626 
notifyCallback( int sessionId, AutofillId id, @AutofillCallback.AutofillEventType int event)2627     private void notifyCallback(
2628             int sessionId, AutofillId id, @AutofillCallback.AutofillEventType int event) {
2629         if (sVerbose) {
2630             Log.v(TAG, "notifyCallback(): sessionId=" + sessionId + ", autofillId=" + id
2631                     + ", event=" + event);
2632         }
2633         final View anchor = findView(id);
2634         if (anchor == null) {
2635             return;
2636         }
2637 
2638         AutofillCallback callback = null;
2639         synchronized (mLock) {
2640             if (mSessionId == sessionId && getClient() != null) {
2641                 callback = mCallback;
2642             }
2643         }
2644 
2645         if (callback != null) {
2646             if (id.isVirtualInt()) {
2647                 callback.onAutofillEvent(
2648                         anchor, id.getVirtualChildIntId(), event);
2649             } else {
2650                 callback.onAutofillEvent(anchor, event);
2651             }
2652         }
2653     }
2654 
2655     /**
2656      * Find a single view by its id.
2657      *
2658      * @param autofillId The autofill id of the view
2659      *
2660      * @return The view or {@code null} if view was not found
2661      */
findView(@onNull AutofillId autofillId)2662     private View findView(@NonNull AutofillId autofillId) {
2663         final AutofillClient client = getClient();
2664         if (client != null) {
2665             return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
2666         }
2667         return null;
2668     }
2669 
2670     /** @hide */
hasAutofillFeature()2671     public boolean hasAutofillFeature() {
2672         return mService != null;
2673     }
2674 
2675     /** @hide */
onPendingSaveUi(int operation, IBinder token)2676     public void onPendingSaveUi(int operation, IBinder token) {
2677         if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2678 
2679         synchronized (mLock) {
2680             try {
2681                 mService.onPendingSaveUi(operation, token);
2682             } catch (RemoteException e) {
2683                 Log.e(TAG, "Error in onPendingSaveUi: ", e);
2684             }
2685         }
2686     }
2687 
2688     /** @hide */
dump(String outerPrefix, PrintWriter pw)2689     public void dump(String outerPrefix, PrintWriter pw) {
2690         pw.print(outerPrefix); pw.println("AutofillManager:");
2691         final String pfx = outerPrefix + "  ";
2692         pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
2693         pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
2694         pw.print(pfx); pw.print("context: "); pw.println(mContext);
2695         final AutofillClient client = getClient();
2696         if (client != null) {
2697             pw.print(pfx); pw.print("client: "); pw.print(client);
2698             pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2699         }
2700         pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
2701         pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
2702         pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2703         pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
2704         pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
2705         pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
2706         pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
2707         pw.print(pfx); pw.print("tracked views: ");
2708         if (mTrackedViews == null) {
2709             pw.println("null");
2710         } else {
2711             final String pfx2 = pfx + "  ";
2712             pw.println();
2713             pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2714             pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2715         }
2716         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
2717         pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
2718         if (mEnteredForAugmentedAutofillIds != null) {
2719             pw.print(pfx); pw.print("entered ids for augmented autofill: ");
2720             pw.println(mEnteredForAugmentedAutofillIds);
2721         }
2722         if (mForAugmentedAutofillOnly) {
2723             pw.print(pfx); pw.println("For Augmented Autofill Only");
2724         }
2725         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2726         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
2727         if (mOptions != null) {
2728             pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
2729         }
2730         pw.print(pfx); pw.print("compat mode enabled: ");
2731         synchronized (mLock) {
2732             if (mCompatibilityBridge != null) {
2733                 final String pfx2 = pfx + "  ";
2734                 pw.println("true");
2735                 pw.print(pfx2); pw.print("windowId: ");
2736                 pw.println(mCompatibilityBridge.mFocusedWindowId);
2737                 pw.print(pfx2); pw.print("nodeId: ");
2738                 pw.println(mCompatibilityBridge.mFocusedNodeId);
2739                 pw.print(pfx2); pw.print("virtualId: ");
2740                 pw.println(AccessibilityNodeInfo
2741                         .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2742                 pw.print(pfx2); pw.print("focusedBounds: ");
2743                 pw.println(mCompatibilityBridge.mFocusedBounds);
2744             } else {
2745                 pw.println("false");
2746             }
2747         }
2748         pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2749         pw.print(" verbose: "); pw.println(sVerbose);
2750     }
2751 
2752     @GuardedBy("mLock")
getStateAsStringLocked()2753     private String getStateAsStringLocked() {
2754         return getStateAsString(mState);
2755     }
2756 
2757     @NonNull
getStateAsString(int state)2758     private static String getStateAsString(int state) {
2759         switch (state) {
2760             case STATE_UNKNOWN:
2761                 return "UNKNOWN";
2762             case STATE_ACTIVE:
2763                 return "ACTIVE";
2764             case STATE_FINISHED:
2765                 return "FINISHED";
2766             case STATE_SHOWING_SAVE_UI:
2767                 return "SHOWING_SAVE_UI";
2768             case STATE_DISABLED_BY_SERVICE:
2769                 return "DISABLED_BY_SERVICE";
2770             case STATE_UNKNOWN_COMPAT_MODE:
2771                 return "UNKNOWN_COMPAT_MODE";
2772             case STATE_UNKNOWN_FAILED:
2773                 return "UNKNOWN_FAILED";
2774             default:
2775                 return "INVALID:" + state;
2776         }
2777     }
2778 
2779     /** @hide */
getSmartSuggestionModeToString(@martSuggestionMode int flags)2780     public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
2781         switch (flags) {
2782             case FLAG_SMART_SUGGESTION_OFF:
2783                 return "OFF";
2784             case FLAG_SMART_SUGGESTION_SYSTEM:
2785                 return "SYSTEM";
2786             default:
2787                 return "INVALID:" + flags;
2788         }
2789     }
2790 
2791     @GuardedBy("mLock")
isActiveLocked()2792     private boolean isActiveLocked() {
2793         return mState == STATE_ACTIVE;
2794     }
2795 
2796     @GuardedBy("mLock")
isDisabledByServiceLocked()2797     private boolean isDisabledByServiceLocked() {
2798         return mState == STATE_DISABLED_BY_SERVICE;
2799     }
2800 
2801     @GuardedBy("mLock")
isFinishedLocked()2802     private boolean isFinishedLocked() {
2803         return mState == STATE_FINISHED;
2804     }
2805 
post(Runnable runnable)2806     private void post(Runnable runnable) {
2807         final AutofillClient client = getClient();
2808         if (client == null) {
2809             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2810             return;
2811         }
2812         client.autofillClientRunOnUiThread(runnable);
2813     }
2814 
2815     /**
2816      * Implementation of the accessibility based compatibility.
2817      */
2818     private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2819         @GuardedBy("mLock")
2820         private final Rect mFocusedBounds = new Rect();
2821         @GuardedBy("mLock")
2822         private final Rect mTempBounds = new Rect();
2823 
2824         @GuardedBy("mLock")
2825         private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2826         @GuardedBy("mLock")
2827         private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2828 
2829         // Need to report a fake service in case a11y clients check the service list
2830         @NonNull
2831         @GuardedBy("mLock")
2832         AccessibilityServiceInfo mCompatServiceInfo;
2833 
CompatibilityBridge()2834         CompatibilityBridge() {
2835             final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2836             am.setAccessibilityPolicy(this);
2837         }
2838 
getCompatServiceInfo()2839         private AccessibilityServiceInfo getCompatServiceInfo() {
2840             synchronized (mLock) {
2841                 if (mCompatServiceInfo != null) {
2842                     return mCompatServiceInfo;
2843                 }
2844                 final Intent intent = new Intent();
2845                 intent.setComponent(new ComponentName("android",
2846                         "com.android.server.autofill.AutofillCompatAccessibilityService"));
2847                 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2848                         intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2849                 try {
2850                     mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2851                 } catch (XmlPullParserException | IOException e) {
2852                     Log.e(TAG, "Cannot find compat autofill service:" + intent);
2853                     throw new IllegalStateException("Cannot find compat autofill service");
2854                 }
2855                 return mCompatServiceInfo;
2856             }
2857         }
2858 
2859         @Override
isEnabled(boolean accessibilityEnabled)2860         public boolean isEnabled(boolean accessibilityEnabled) {
2861             return true;
2862         }
2863 
2864         @Override
getRelevantEventTypes(int relevantEventTypes)2865         public int getRelevantEventTypes(int relevantEventTypes) {
2866             return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2867                     | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
2868                     | AccessibilityEvent.TYPE_VIEW_CLICKED
2869                     | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2870         }
2871 
2872         @Override
getInstalledAccessibilityServiceList( List<AccessibilityServiceInfo> installedServices)2873         public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2874                List<AccessibilityServiceInfo> installedServices) {
2875             if (installedServices == null) {
2876                 installedServices = new ArrayList<>();
2877             }
2878             installedServices.add(getCompatServiceInfo());
2879             return installedServices;
2880         }
2881 
2882         @Override
getEnabledAccessibilityServiceList( int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService)2883         public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2884                 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2885             if (enabledService == null) {
2886                 enabledService = new ArrayList<>();
2887             }
2888             enabledService.add(getCompatServiceInfo());
2889             return enabledService;
2890         }
2891 
2892         @Override
onAccessibilityEvent(AccessibilityEvent event, boolean accessibilityEnabled, int relevantEventTypes)2893         public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2894                 boolean accessibilityEnabled, int relevantEventTypes) {
2895             final int type = event.getEventType();
2896             if (sVerbose) {
2897                 // NOTE: this is waaay spammy, but that's life.
2898                 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
2899                         + "): virtualId="
2900                         + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2901                         + ", client=" + getClient());
2902             }
2903             switch (type) {
2904                 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2905                     synchronized (mLock) {
2906                         if (mFocusedWindowId == event.getWindowId()
2907                                 && mFocusedNodeId == event.getSourceNodeId()) {
2908                             return event;
2909                         }
2910                         if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2911                                 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2912                             notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2913                             mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2914                             mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2915                             mFocusedBounds.set(0, 0, 0, 0);
2916                         }
2917                         final int windowId = event.getWindowId();
2918                         final long nodeId = event.getSourceNodeId();
2919                         if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2920                             mFocusedWindowId = windowId;
2921                             mFocusedNodeId = nodeId;
2922                         }
2923                     }
2924                 } break;
2925 
2926                 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2927                     synchronized (mLock) {
2928                         if (mFocusedWindowId == event.getWindowId()
2929                                 && mFocusedNodeId == event.getSourceNodeId()) {
2930                             notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2931                         }
2932                     }
2933                 } break;
2934 
2935                 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2936                     synchronized (mLock) {
2937                         notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2938                     }
2939                 } break;
2940 
2941                 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2942                     final AutofillClient client = getClient();
2943                     if (client != null) {
2944                         synchronized (mLock) {
2945                             if (client.autofillClientIsFillUiShowing()) {
2946                                 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2947                             }
2948                             updateTrackedViewsLocked();
2949                         }
2950                     }
2951                 } break;
2952             }
2953 
2954             return accessibilityEnabled ? event : null;
2955         }
2956 
notifyViewEntered(int windowId, long nodeId, Rect focusedBounds)2957         private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2958             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2959             if (!isVirtualNode(virtualId)) {
2960                 return false;
2961             }
2962             final View view = findViewByAccessibilityId(windowId, nodeId);
2963             if (view == null) {
2964                 return false;
2965             }
2966             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2967             if (node == null) {
2968                 return false;
2969             }
2970             if (!node.isEditable()) {
2971                 return false;
2972             }
2973             final Rect newBounds = mTempBounds;
2974             node.getBoundsInScreen(newBounds);
2975             if (newBounds.equals(focusedBounds)) {
2976                 return false;
2977             }
2978             focusedBounds.set(newBounds);
2979             AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2980             return true;
2981         }
2982 
notifyViewExited(int windowId, long nodeId)2983         private void notifyViewExited(int windowId, long nodeId) {
2984             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2985             if (!isVirtualNode(virtualId)) {
2986                 return;
2987             }
2988             final View view = findViewByAccessibilityId(windowId, nodeId);
2989             if (view == null) {
2990                 return;
2991             }
2992             AutofillManager.this.notifyViewExited(view, virtualId);
2993         }
2994 
notifyValueChanged(int windowId, long nodeId)2995         private void notifyValueChanged(int windowId, long nodeId) {
2996             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2997             if (!isVirtualNode(virtualId)) {
2998                 return;
2999             }
3000             final View view = findViewByAccessibilityId(windowId, nodeId);
3001             if (view == null) {
3002                 return;
3003             }
3004             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
3005             if (node == null) {
3006                 return;
3007             }
3008             AutofillManager.this.notifyValueChanged(view, virtualId,
3009                     AutofillValue.forText(node.getText()));
3010         }
3011 
notifyViewClicked(int windowId, long nodeId)3012         private void notifyViewClicked(int windowId, long nodeId) {
3013             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
3014             if (!isVirtualNode(virtualId)) {
3015                 return;
3016             }
3017             final View view = findViewByAccessibilityId(windowId, nodeId);
3018             if (view == null) {
3019                 return;
3020             }
3021             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
3022             if (node == null) {
3023                 return;
3024             }
3025             AutofillManager.this.notifyViewClicked(view, virtualId);
3026         }
3027 
3028         @GuardedBy("mLock")
updateTrackedViewsLocked()3029         private void updateTrackedViewsLocked() {
3030             if (mTrackedViews != null) {
3031                 mTrackedViews.onVisibleForAutofillChangedLocked();
3032             }
3033         }
3034 
findViewByAccessibilityId(int windowId, long nodeId)3035         private View findViewByAccessibilityId(int windowId, long nodeId) {
3036             final AutofillClient client = getClient();
3037             if (client == null) {
3038                 return null;
3039             }
3040             final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
3041             return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
3042         }
3043 
findVirtualNodeByAccessibilityId(View view, int virtualId)3044         private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
3045             final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
3046             if (provider == null) {
3047                 return null;
3048             }
3049             return provider.createAccessibilityNodeInfo(virtualId);
3050         }
3051 
isVirtualNode(int nodeId)3052         private boolean isVirtualNode(int nodeId) {
3053             return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
3054                     && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
3055         }
3056     }
3057 
3058     /**
3059      * View tracking information. Once all tracked views become invisible the session is finished.
3060      */
3061     private class TrackedViews {
3062         /** Visible tracked views */
3063         @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
3064 
3065         /** Invisible tracked views */
3066         @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
3067 
3068         /**
3069          * Check if set is null or value is in set.
3070          *
3071          * @param set   The set or null (== empty set)
3072          * @param value The value that might be in the set
3073          *
3074          * @return {@code true} iff set is not empty and value is in set
3075          */
3076         // TODO: move to Helper as static method
isInSet(@ullable ArraySet<T> set, T value)3077         private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
3078             return set != null && set.contains(value);
3079         }
3080 
3081         /**
3082          * Add a value to a set. If set is null, create a new set.
3083          *
3084          * @param set        The set or null (== empty set)
3085          * @param valueToAdd The value to add
3086          *
3087          * @return The set including the new value. If set was {@code null}, a set containing only
3088          *         the new value.
3089          */
3090         // TODO: move to Helper as static method
3091         @NonNull
addToSet(@ullable ArraySet<T> set, T valueToAdd)3092         private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
3093             if (set == null) {
3094                 set = new ArraySet<>(1);
3095             }
3096 
3097             set.add(valueToAdd);
3098 
3099             return set;
3100         }
3101 
3102         /**
3103          * Remove a value from a set.
3104          *
3105          * @param set           The set or null (== empty set)
3106          * @param valueToRemove The value to remove
3107          *
3108          * @return The set without the removed value. {@code null} if set was null, or is empty
3109          *         after removal.
3110          */
3111         // TODO: move to Helper as static method
3112         @Nullable
removeFromSet(@ullable ArraySet<T> set, T valueToRemove)3113         private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
3114             if (set == null) {
3115                 return null;
3116             }
3117 
3118             set.remove(valueToRemove);
3119 
3120             if (set.isEmpty()) {
3121                 return null;
3122             }
3123 
3124             return set;
3125         }
3126 
3127         /**
3128          * Set the tracked views.
3129          *
3130          * @param trackedIds The views to be tracked
3131          */
TrackedViews(@ullable AutofillId[] trackedIds)3132         TrackedViews(@Nullable AutofillId[] trackedIds) {
3133             final AutofillClient client = getClient();
3134             if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
3135                 final boolean[] isVisible;
3136 
3137                 if (client.autofillClientIsVisibleForAutofill()) {
3138                     if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
3139                     isVisible = client.autofillClientGetViewVisibility(trackedIds);
3140                 } else {
3141                     // All false
3142                     isVisible = new boolean[trackedIds.length];
3143                 }
3144 
3145                 final int numIds = trackedIds.length;
3146                 for (int i = 0; i < numIds; i++) {
3147                     final AutofillId id = trackedIds[i];
3148                     id.resetSessionId();
3149 
3150                     if (isVisible[i]) {
3151                         mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
3152                     } else {
3153                         mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3154                     }
3155                 }
3156             }
3157 
3158             if (sVerbose) {
3159                 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
3160                         + " mVisibleTrackedIds=" + mVisibleTrackedIds
3161                         + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
3162             }
3163 
3164             if (mVisibleTrackedIds == null) {
3165                 finishSessionLocked();
3166             }
3167         }
3168 
3169         /**
3170          * Called when a {@link View view's} visibility changes.
3171          *
3172          * @param id the id of the view/virtual view whose visibility changed.
3173          * @param isVisible visible if the view is visible in the view hierarchy.
3174          */
3175         @GuardedBy("mLock")
notifyViewVisibilityChangedLocked(@onNull AutofillId id, boolean isVisible)3176         void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
3177             if (sDebug) {
3178                 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
3179                         + isVisible);
3180             }
3181 
3182             if (isClientVisibleForAutofillLocked()) {
3183                 if (isVisible) {
3184                     if (isInSet(mInvisibleTrackedIds, id)) {
3185                         mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
3186                         mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
3187                     }
3188                 } else {
3189                     if (isInSet(mVisibleTrackedIds, id)) {
3190                         mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
3191                         mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3192                     }
3193                 }
3194             }
3195 
3196             if (mVisibleTrackedIds == null) {
3197                 if (sVerbose) {
3198                     Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
3199                 }
3200                 finishSessionLocked();
3201             }
3202         }
3203 
3204         /**
3205          * Called once the client becomes visible.
3206          *
3207          * @see AutofillClient#autofillClientIsVisibleForAutofill()
3208          */
3209         @GuardedBy("mLock")
onVisibleForAutofillChangedLocked()3210         void onVisibleForAutofillChangedLocked() {
3211             // The visibility of the views might have changed while the client was not be visible,
3212             // hence update the visibility state for all views.
3213             AutofillClient client = getClient();
3214             ArraySet<AutofillId> updatedVisibleTrackedIds = null;
3215             ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
3216             if (client != null) {
3217                 if (sVerbose) {
3218                     Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
3219                             + " vis=" + mVisibleTrackedIds);
3220                 }
3221                 if (mInvisibleTrackedIds != null) {
3222                     final ArrayList<AutofillId> orderedInvisibleIds =
3223                             new ArrayList<>(mInvisibleTrackedIds);
3224                     final boolean[] isVisible = client.autofillClientGetViewVisibility(
3225                             Helper.toArray(orderedInvisibleIds));
3226 
3227                     final int numInvisibleTrackedIds = orderedInvisibleIds.size();
3228                     for (int i = 0; i < numInvisibleTrackedIds; i++) {
3229                         final AutofillId id = orderedInvisibleIds.get(i);
3230                         if (isVisible[i]) {
3231                             updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3232 
3233                             if (sDebug) {
3234                                 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
3235                             }
3236                         } else {
3237                             updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3238                         }
3239                     }
3240                 }
3241 
3242                 if (mVisibleTrackedIds != null) {
3243                     final ArrayList<AutofillId> orderedVisibleIds =
3244                             new ArrayList<>(mVisibleTrackedIds);
3245                     final boolean[] isVisible = client.autofillClientGetViewVisibility(
3246                             Helper.toArray(orderedVisibleIds));
3247 
3248                     final int numVisibleTrackedIds = orderedVisibleIds.size();
3249                     for (int i = 0; i < numVisibleTrackedIds; i++) {
3250                         final AutofillId id = orderedVisibleIds.get(i);
3251 
3252                         if (isVisible[i]) {
3253                             updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3254                         } else {
3255                             updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3256 
3257                             if (sDebug) {
3258                                 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
3259                             }
3260                         }
3261                     }
3262                 }
3263 
3264                 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
3265                 mVisibleTrackedIds = updatedVisibleTrackedIds;
3266             }
3267 
3268             if (mVisibleTrackedIds == null) {
3269                 if (sVerbose) {
3270                     Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
3271                 }
3272                 finishSessionLocked();
3273             }
3274         }
3275     }
3276 
3277     /**
3278      * Callback for autofill related events.
3279      *
3280      * <p>Typically used for applications that display their own "auto-complete" views, so they can
3281      * enable / disable such views when the autofill UI is shown / hidden.
3282      */
3283     public abstract static class AutofillCallback {
3284 
3285         /** @hide */
3286         @IntDef(prefix = { "EVENT_INPUT_" }, value = {
3287                 EVENT_INPUT_SHOWN,
3288                 EVENT_INPUT_HIDDEN,
3289                 EVENT_INPUT_UNAVAILABLE
3290         })
3291         @Retention(RetentionPolicy.SOURCE)
3292         public @interface AutofillEventType {}
3293 
3294         /**
3295          * The autofill input UI associated with the view was shown.
3296          *
3297          * <p>If the view provides its own auto-complete UI and its currently shown, it
3298          * should be hidden upon receiving this event.
3299          */
3300         public static final int EVENT_INPUT_SHOWN = 1;
3301 
3302         /**
3303          * The autofill input UI associated with the view was hidden.
3304          *
3305          * <p>If the view provides its own auto-complete UI that was hidden upon a
3306          * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
3307          */
3308         public static final int EVENT_INPUT_HIDDEN = 2;
3309 
3310         /**
3311          * The autofill input UI associated with the view isn't shown because
3312          * autofill is not available.
3313          *
3314          * <p>If the view provides its own auto-complete UI but was not displaying it
3315          * to avoid flickering, it could shown it upon receiving this event.
3316          */
3317         public static final int EVENT_INPUT_UNAVAILABLE = 3;
3318 
3319         /**
3320          * Called after a change in the autofill state associated with a view.
3321          *
3322          * @param view view associated with the change.
3323          *
3324          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3325          */
onAutofillEvent(@onNull View view, @AutofillEventType int event)3326         public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
3327         }
3328 
3329         /**
3330          * Called after a change in the autofill state associated with a virtual view.
3331          *
3332          * @param view parent view associated with the change.
3333          * @param virtualId id identifying the virtual child inside the parent view.
3334          *
3335          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3336          */
onAutofillEvent(@onNull View view, int virtualId, @AutofillEventType int event)3337         public void onAutofillEvent(@NonNull View view, int virtualId,
3338                 @AutofillEventType int event) {
3339         }
3340     }
3341 
3342     private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
3343         private final WeakReference<AutofillManager> mAfm;
3344 
AutofillManagerClient(AutofillManager autofillManager)3345         private AutofillManagerClient(AutofillManager autofillManager) {
3346             mAfm = new WeakReference<>(autofillManager);
3347         }
3348 
3349         @Override
setState(int flags)3350         public void setState(int flags) {
3351             final AutofillManager afm = mAfm.get();
3352             if (afm != null) {
3353                 afm.post(() -> afm.setState(flags));
3354             }
3355         }
3356 
3357         @Override
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)3358         public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3359                 boolean hideHighlight) {
3360             final AutofillManager afm = mAfm.get();
3361             if (afm != null) {
3362                 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
3363             }
3364         }
3365 
3366         @Override
authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)3367         public void authenticate(int sessionId, int authenticationId, IntentSender intent,
3368                 Intent fillInIntent, boolean authenticateInline) {
3369             final AutofillManager afm = mAfm.get();
3370             if (afm != null) {
3371                 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
3372                         authenticateInline));
3373             }
3374         }
3375 
3376         @Override
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)3377         public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3378                 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3379             final AutofillManager afm = mAfm.get();
3380             if (afm != null) {
3381                 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3382                         presenter));
3383             }
3384         }
3385 
3386         @Override
requestHideFillUi(int sessionId, AutofillId id)3387         public void requestHideFillUi(int sessionId, AutofillId id) {
3388             final AutofillManager afm = mAfm.get();
3389             if (afm != null) {
3390                 afm.post(() -> afm.requestHideFillUi(id, false));
3391             }
3392         }
3393 
3394         @Override
notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState)3395         public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
3396             final AutofillManager afm = mAfm.get();
3397             if (afm != null) {
3398                 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
3399             }
3400         }
3401 
3402         @Override
notifyFillUiShown(int sessionId, AutofillId id)3403         public void notifyFillUiShown(int sessionId, AutofillId id) {
3404             final AutofillManager afm = mAfm.get();
3405             if (afm != null) {
3406                 afm.post(
3407                         () -> afm.notifyCallback(
3408                                 sessionId, id, AutofillCallback.EVENT_INPUT_SHOWN));
3409             }
3410         }
3411 
3412         @Override
notifyFillUiHidden(int sessionId, AutofillId id)3413         public void notifyFillUiHidden(int sessionId, AutofillId id) {
3414             final AutofillManager afm = mAfm.get();
3415             if (afm != null) {
3416                 afm.post(
3417                         () -> afm.notifyCallback(
3418                                 sessionId, id, AutofillCallback.EVENT_INPUT_HIDDEN));
3419             }
3420         }
3421 
3422         @Override
notifyDisableAutofill(long disableDuration, ComponentName componentName)3423         public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
3424                 throws RemoteException {
3425             final AutofillManager afm = mAfm.get();
3426             if (afm != null) {
3427                 afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
3428             }
3429         }
3430 
3431         @Override
dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen)3432         public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
3433             final AutofillManager afm = mAfm.get();
3434             if (afm != null) {
3435                 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
3436             }
3437         }
3438 
3439         @Override
startIntentSender(IntentSender intentSender, Intent intent)3440         public void startIntentSender(IntentSender intentSender, Intent intent) {
3441             final AutofillManager afm = mAfm.get();
3442             if (afm != null) {
3443                 afm.post(() -> {
3444                     try {
3445                         afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
3446                     } catch (IntentSender.SendIntentException e) {
3447                         Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
3448                     }
3449                 });
3450             }
3451         }
3452 
3453         @Override
setTrackedViews(int sessionId, AutofillId[] ids, boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds, AutofillId saveTriggerId)3454         public void setTrackedViews(int sessionId, AutofillId[] ids,
3455                 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
3456                 AutofillId saveTriggerId) {
3457             final AutofillManager afm = mAfm.get();
3458             if (afm != null) {
3459                 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
3460                         saveOnFinish, fillableIds, saveTriggerId));
3461             }
3462         }
3463 
3464         @Override
setSaveUiState(int sessionId, boolean shown)3465         public void setSaveUiState(int sessionId, boolean shown) {
3466             final AutofillManager afm = mAfm.get();
3467             if (afm != null) {
3468                 afm.post(() -> afm.setSaveUiState(sessionId, shown));
3469             }
3470         }
3471 
3472         @Override
setSessionFinished(int newState, List<AutofillId> autofillableIds)3473         public void setSessionFinished(int newState, List<AutofillId> autofillableIds) {
3474             final AutofillManager afm = mAfm.get();
3475             if (afm != null) {
3476                 afm.post(() -> afm.setSessionFinished(newState, autofillableIds));
3477             }
3478         }
3479 
3480         @Override
getAugmentedAutofillClient(IResultReceiver result)3481         public void getAugmentedAutofillClient(IResultReceiver result) {
3482             final AutofillManager afm = mAfm.get();
3483             if (afm != null) {
3484                 afm.post(() -> afm.getAugmentedAutofillClient(result));
3485             }
3486         }
3487 
3488         @Override
requestShowSoftInput(@onNull AutofillId id)3489         public void requestShowSoftInput(@NonNull AutofillId id) {
3490             final AutofillManager afm = mAfm.get();
3491             if (afm != null) {
3492                 afm.post(() -> afm.requestShowSoftInput(id));
3493             }
3494         }
3495     }
3496 
3497     private static final class AugmentedAutofillManagerClient
3498             extends IAugmentedAutofillManagerClient.Stub {
3499         private final WeakReference<AutofillManager> mAfm;
3500 
AugmentedAutofillManagerClient(AutofillManager autofillManager)3501         private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
3502             mAfm = new WeakReference<>(autofillManager);
3503         }
3504 
3505         @Override
getViewCoordinates(@onNull AutofillId id)3506         public Rect getViewCoordinates(@NonNull AutofillId id) {
3507             final AutofillManager afm = mAfm.get();
3508             if (afm == null) return null;
3509 
3510             final View view = getView(afm, id);
3511             if (view == null) {
3512                 return null;
3513             }
3514             final Rect windowVisibleDisplayFrame = new Rect();
3515             view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
3516             final int[] location = new int[2];
3517             view.getLocationOnScreen(location);
3518             final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
3519                     location[0] + view.getWidth(),
3520                     location[1] - windowVisibleDisplayFrame.top + view.getHeight());
3521             if (sVerbose) {
3522                 Log.v(TAG, "Coordinates for " + id + ": " + rect);
3523             }
3524             return rect;
3525         }
3526 
3527         @Override
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)3528         public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3529                 boolean hideHighlight) {
3530             final AutofillManager afm = mAfm.get();
3531             if (afm != null) {
3532                 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
3533             }
3534         }
3535 
3536         @Override
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)3537         public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3538                 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3539             final AutofillManager afm = mAfm.get();
3540             if (afm != null) {
3541                 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3542                         presenter));
3543             }
3544         }
3545 
3546         @Override
requestHideFillUi(int sessionId, AutofillId id)3547         public void requestHideFillUi(int sessionId, AutofillId id) {
3548             final AutofillManager afm = mAfm.get();
3549             if (afm != null) {
3550                 afm.post(() -> afm.requestHideFillUi(id, false));
3551             }
3552         }
3553 
3554         @Override
requestAutofill(int sessionId, AutofillId id)3555         public boolean requestAutofill(int sessionId, AutofillId id) {
3556             final AutofillManager afm = mAfm.get();
3557             if (afm == null || afm.mSessionId != sessionId) {
3558                 if (sDebug) {
3559                     Slog.d(TAG, "Autofill not available or sessionId doesn't match");
3560                 }
3561                 return false;
3562             }
3563             final View view = getView(afm, id);
3564             if (view == null || !view.isFocused()) {
3565                 if (sDebug) {
3566                     Slog.d(TAG, "View not available or is not on focus");
3567                 }
3568                 return false;
3569             }
3570             if (sVerbose) {
3571                 Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
3572             }
3573             afm.post(() -> afm.requestAutofillFromNewSession(view));
3574             return true;
3575         }
3576 
3577         @Nullable
getView(@onNull AutofillManager afm, @NonNull AutofillId id)3578         private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
3579             final AutofillClient client = afm.getClient();
3580             if (client == null) {
3581                 Log.w(TAG, "getView(" + id + "): no autofill client");
3582                 return null;
3583             }
3584             View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3585             if (view == null) {
3586                 Log.w(TAG, "getView(" + id + "): could not find view");
3587             }
3588             return view;
3589         }
3590     }
3591 }
3592