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