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_IME_SHOWING;
20 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
21 import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
22 import static android.service.autofill.FillRequest.FLAG_PCC_DETECTION;
23 import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE;
24 import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD;
25 import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
26 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
27 import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
28 import static android.view.ContentInfo.SOURCE_AUTOFILL;
29 import static android.view.autofill.Helper.sDebug;
30 import static android.view.autofill.Helper.sVerbose;
31 import static android.view.autofill.Helper.toList;
32 
33 import android.accessibilityservice.AccessibilityServiceInfo;
34 import android.annotation.IntDef;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.RequiresFeature;
38 import android.annotation.SystemApi;
39 import android.annotation.SystemService;
40 import android.annotation.TestApi;
41 import android.app.ActivityOptions;
42 import android.app.assist.AssistStructure.ViewNode;
43 import android.app.assist.AssistStructure.ViewNodeBuilder;
44 import android.app.assist.AssistStructure.ViewNodeParcelable;
45 import android.content.AutofillOptions;
46 import android.content.ClipData;
47 import android.content.ComponentName;
48 import android.content.Context;
49 import android.content.Intent;
50 import android.content.IntentSender;
51 import android.content.pm.PackageManager;
52 import android.content.pm.ResolveInfo;
53 import android.credentials.GetCredentialException;
54 import android.credentials.GetCredentialResponse;
55 import android.graphics.Rect;
56 import android.metrics.LogMaker;
57 import android.os.Build;
58 import android.os.Bundle;
59 import android.os.Handler;
60 import android.os.IBinder;
61 import android.os.Looper;
62 import android.os.Parcelable;
63 import android.os.RemoteException;
64 import android.os.SystemClock;
65 import android.service.autofill.AutofillService;
66 import android.service.autofill.FillEventHistory;
67 import android.service.autofill.Flags;
68 import android.service.autofill.UserData;
69 import android.service.credentials.CredentialProviderService;
70 import android.text.TextUtils;
71 import android.util.ArrayMap;
72 import android.util.ArraySet;
73 import android.util.DebugUtils;
74 import android.util.Log;
75 import android.util.Slog;
76 import android.util.SparseArray;
77 import android.view.Choreographer;
78 import android.view.ContentInfo;
79 import android.view.KeyEvent;
80 import android.view.View;
81 import android.view.ViewRootImpl;
82 import android.view.WindowInsets;
83 import android.view.WindowManager;
84 import android.view.accessibility.AccessibilityEvent;
85 import android.view.accessibility.AccessibilityManager;
86 import android.view.accessibility.AccessibilityNodeInfo;
87 import android.view.accessibility.AccessibilityNodeProvider;
88 import android.view.accessibility.AccessibilityWindowInfo;
89 import android.view.inputmethod.InputMethodManager;
90 import android.widget.CheckBox;
91 import android.widget.DatePicker;
92 import android.widget.EditText;
93 import android.widget.RadioGroup;
94 import android.widget.Spinner;
95 import android.widget.TextView;
96 import android.widget.TimePicker;
97 
98 import com.android.internal.annotations.GuardedBy;
99 import com.android.internal.logging.MetricsLogger;
100 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
101 import com.android.internal.os.IResultReceiver;
102 import com.android.internal.util.ArrayUtils;
103 import com.android.internal.util.SyncResultReceiver;
104 
105 import org.xmlpull.v1.XmlPullParserException;
106 
107 import java.io.IOException;
108 import java.io.PrintWriter;
109 import java.io.Serializable;
110 import java.lang.annotation.Retention;
111 import java.lang.annotation.RetentionPolicy;
112 import java.lang.ref.WeakReference;
113 import java.util.ArrayList;
114 import java.util.Arrays;
115 import java.util.Collections;
116 import java.util.List;
117 import java.util.Objects;
118 import java.util.Set;
119 import java.util.concurrent.atomic.AtomicBoolean;
120 
121 import sun.misc.Cleaner;
122 
123 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
124 
125 /**
126  * <p>The {@link AutofillManager} class provides ways for apps and custom views to
127  * integrate with the Autofill Framework lifecycle.
128  *
129  * <p>To learn about using Autofill in your app, read
130  * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
131  *
132  * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
133  *
134  * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
135  * activity context. The autofill context is created when one of the following methods is called for
136  * the first time in an activity context, and the current user has an enabled autofill service:
137  *
138  * <ul>
139  *   <li>{@link #notifyViewEntered(View)}
140  *   <li>{@link #notifyViewEntered(View, int, Rect)}
141  *   <li>{@link #requestAutofill(View)}
142  * </ul>
143  *
144  * <p>Typically, the context is automatically created when the first view of the activity is
145  * focused because {@code View.onFocusChanged()} indirectly calls
146  * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
147  * explicitly create it (for example, a custom view developer could offer a contextual menu action
148  * in a text-field view to let users manually request autofill).
149  *
150  * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
151  * that represents the view hierarchy by calling
152  * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
153  * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
154  * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
155  * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
156  * the hierarchy.
157  *
158  * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
159  * parses it looking for views that can be autofilled. If the service finds such views, it returns
160  * a data structure to the Android System containing the following optional info:
161  *
162  * <ul>
163  *   <li>Datasets used to autofill subsets of views in the activity.
164  *   <li>Id of views that the service can save their values for future autofilling.
165  * </ul>
166  *
167  * <p>When the service returns datasets, the Android System displays an autofill dataset picker
168  * UI associated with the view, when the view is focused on and is part of a dataset.
169  * The application can be notified when the UI is shown by registering an
170  * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
171  * selects a dataset from the UI, all views present in the dataset are autofilled, through
172  * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
173  *
174  * <p>When the service returns ids of savable views, the Android System keeps track of changes
175  * made to these views, so they can be used to determine if the autofill save UI is shown later.
176  *
177  * <p>The context is then finished when one of the following occurs:
178  *
179  * <ul>
180  *   <li>{@link #commit()} is called or all savable views are gone.
181  *   <li>{@link #cancel()} is called.
182  * </ul>
183  *
184  * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
185  * shows an autofill save UI if the value of savable views have changed. If the user selects the
186  * option to Save, the current value of the views is then sent to the autofill service.
187  *
188  * <h3 id="additional-notes">Additional notes</h3>
189  *
190  * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
191  */
192 @SystemService(Context.AUTOFILL_MANAGER_SERVICE)
193 @RequiresFeature(PackageManager.FEATURE_AUTOFILL)
194 public final class AutofillManager {
195 
196     private static final boolean DBG = false;
197     private static final String TAG = "AutofillManager";
198 
199     /**
200      * Intent extra: The assist structure which captures the filled screen.
201      *
202      * <p>
203      * Type: {@link android.app.assist.AssistStructure}
204      */
205     public static final String EXTRA_ASSIST_STRUCTURE =
206             "android.view.autofill.extra.ASSIST_STRUCTURE";
207 
208     /**
209      * Intent extra: The result of an authentication operation. It is
210      * either a fully populated {@link android.service.autofill.FillResponse}
211      * or a fully populated {@link android.service.autofill.Dataset} if
212      * a response or a dataset is being authenticated respectively.
213      *
214      * <p>
215      * Type: {@link android.service.autofill.FillResponse} or a
216      * {@link android.service.autofill.Dataset}
217      */
218     public static final String EXTRA_AUTHENTICATION_RESULT =
219             "android.view.autofill.extra.AUTHENTICATION_RESULT";
220 
221     /**
222      * Intent extra: The optional boolean extra field provided by the
223      * {@link android.service.autofill.AutofillService} accompanying the {@link
224      * android.service.autofill.Dataset} result of an authentication operation.
225      *
226      * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a
227      * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also
228      * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}.
229      * That means if the user clears the field values, the autofill suggestion will show up again
230      * with the new authenticated Dataset.
231      *
232      * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior
233      * that if the Dataset being authenticated is a pinned dataset (see
234      * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be
235      * replaced.
236      *
237      * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to
238      * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether
239      * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to
240      * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the
241      * returned Dataset will not replace the old dataset from the existing
242      * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not
243      * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is
244      * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}.
245      */
246     public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET =
247             "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
248 
249     /**
250      * Intent extra: The optional extras provided by the
251      * {@link android.service.autofill.AutofillService}.
252      *
253      * <p>For example, when the service responds to a {@link
254      * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
255      * a {@code FillResponse} that requires authentication, the Intent that launches the
256      * service authentication will contain the Bundle set by
257      * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
258      *
259      * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
260      * can also add this bundle to the {@link Intent} set as the
261      * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
262      * so the bundle can be recovered later on
263      * {@link android.service.autofill.SaveRequest#getClientState()}.
264      *
265      * <p>
266      * Type: {@link android.os.Bundle}
267      */
268     public static final String EXTRA_CLIENT_STATE =
269             "android.view.autofill.extra.CLIENT_STATE";
270 
271     /**
272      * @hide
273      */
274     public static final String EXTRA_AUTH_STATE =
275             "android.view.autofill.extra.AUTH_STATE";
276 
277     /**
278      * Intent extra: the {@link android.view.inputmethod.InlineSuggestionsRequest} in the
279      * autofill request.
280      *
281      * <p>This is filled in the authentication intent so the
282      * {@link android.service.autofill.AutofillService} can use it to create the inline
283      * suggestion {@link android.service.autofill.Dataset} in the response, if the original autofill
284      * request contains the {@link android.view.inputmethod.InlineSuggestionsRequest}.
285      */
286     public static final String EXTRA_INLINE_SUGGESTIONS_REQUEST =
287             "android.view.autofill.extra.INLINE_SUGGESTIONS_REQUEST";
288 
289     /** @hide */
290     public static final String EXTRA_RESTORE_SESSION_TOKEN =
291             "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
292 
293     /** @hide */
294     public static final String EXTRA_RESTORE_CROSS_ACTIVITY =
295             "android.view.autofill.extra.RESTORE_CROSS_ACTIVITY";
296 
297     /**
298      * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
299      *
300      * @hide
301      */
302     public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
303             "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
304 
305     /**
306      * Internal extra used to pass the fill request id in client state of
307      * {@link ConvertCredentialResponse}
308      *
309      * @hide
310      */
311     public static final String EXTRA_AUTOFILL_REQUEST_ID =
312             "android.view.autofill.extra.AUTOFILL_REQUEST_ID";
313 
314     /**
315      * Autofill Hint to indicate that it can match any field.
316      *
317      * @hide
318      */
319     @TestApi
320     public static final String ANY_HINT = "any";
321 
322     private static final String SESSION_ID_TAG = "android:sessionId";
323     private static final String STATE_TAG = "android:state";
324     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
325 
326     /** @hide */ public static final int ACTION_START_SESSION = 1;
327     /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
328     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
329     /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
330     /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
331 
332     /** @hide */ public static final int NO_LOGGING = 0;
333     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
334     /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
335     /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
336     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
337 
338     // NOTE: flag below is used by the session start receiver only, hence it can have values above
339     /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
340 
341     /** @hide */
342     public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
343             ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
344             : AutofillManager.NO_LOGGING;
345 
346     /** @hide */
347     public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
348 
349     /** Which bits in an authentication id are used for the dataset id */
350     private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
351     /** How many bits in an authentication id are used for the dataset id */
352     private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
353     /** @hide The index for an undefined data set */
354     public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
355 
356     /**
357      * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
358      *
359      * @hide
360      */
361     public static final int PENDING_UI_OPERATION_CANCEL = 1;
362 
363     /**
364      * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
365      *
366      * @hide
367      */
368     public static final int PENDING_UI_OPERATION_RESTORE = 2;
369 
370     /**
371      * Initial state of the autofill context, set when there is no session (i.e., when
372      * {@link #mSessionId} is {@link #NO_SESSION}).
373      *
374      * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
375      * the server.
376      *
377      * @hide
378      */
379     public static final int STATE_UNKNOWN = 0;
380 
381     /**
382      * State where the autofill context hasn't been {@link #commit() finished} nor
383      * {@link #cancel() canceled} yet.
384      *
385      * @hide
386      */
387     public static final int STATE_ACTIVE = 1;
388 
389     /**
390      * State where the autofill context was finished by the server because the autofill
391      * service could not autofill the activity.
392      *
393      * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
394      * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
395      *
396      * @hide
397      */
398     public static final int STATE_FINISHED = 2;
399 
400     /**
401      * State where the autofill context has been {@link #commit() finished} but the server still has
402      * a session because the Save UI hasn't been dismissed yet.
403      *
404      * @hide
405      */
406     public static final int STATE_SHOWING_SAVE_UI = 3;
407 
408     /**
409      * State where the autofill is disabled because the service cannot autofill the activity at all.
410      *
411      * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
412      * (and {@link #requestAutofill(View, int, Rect)}).
413      *
414      * @hide
415      */
416     public static final int STATE_DISABLED_BY_SERVICE = 4;
417 
418     /**
419      * Same as {@link #STATE_UNKNOWN}, but used on
420      * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
421      * because the URL bar changed on client mode
422      *
423      * @hide
424      */
425     public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
426 
427     /**
428      * Same as {@link #STATE_UNKNOWN}, but used on
429      * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
430      * because the service failed to fullfil a request.
431      *
432      * @hide
433      */
434     public static final int STATE_UNKNOWN_FAILED = 6;
435 
436     /**
437      * Same as {@link #STATE_ACTIVE}, but when pending authentication after
438      * {@link AutofillManagerClient#authenticate(int, int, IntentSender, Intent, boolean)}
439      *
440      * @hide
441      */
442     public static final int STATE_PENDING_AUTHENTICATION = 7;
443 
444     /**
445      * Timeout in ms for calls to the field classification service.
446      * @hide
447      */
448     public static final int FC_SERVICE_TIMEOUT = 5000;
449 
450     /**
451      * Timeout for calls to system_server.
452      */
453     private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
454 
455     /**
456      * @hide
457      */
458     @TestApi
459     public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
460 
461     /**
462      * Disables Augmented Autofill.
463      *
464      * @hide
465      */
466     @TestApi
467     public static final int FLAG_SMART_SUGGESTION_OFF = 0x0;
468 
469     /**
470      * Displays the Augment Autofill window using the same mechanism (such as a popup-window
471      * attached to the focused view) as the standard autofill.
472      *
473      * @hide
474      */
475     @TestApi
476     public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
477 
478     /** @hide */
479     @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM })
480     @Retention(RetentionPolicy.SOURCE)
481     public @interface SmartSuggestionMode {}
482 
483     /** @hide */
484     public static final int RESULT_OK = 0;
485     /** @hide */
486     public static final int RESULT_CODE_NOT_SERVICE = -1;
487 
488     /**
489      *  Reasons to commit the Autofill context.
490      *
491      *  <p>If adding a new reason, modify
492      *  {@link com.android.server.autofill.PresentationStatsEventLogger#getNoPresentationEventReason(int)}
493      *  as well.</p>
494      *
495      *  @hide
496      */
497     @IntDef(prefix = { "COMMIT_REASON_" }, value = {
498             COMMIT_REASON_UNKNOWN,
499             COMMIT_REASON_ACTIVITY_FINISHED,
500             COMMIT_REASON_VIEW_COMMITTED,
501             COMMIT_REASON_VIEW_CLICKED,
502             COMMIT_REASON_VIEW_CHANGED,
503             COMMIT_REASON_SESSION_DESTROYED
504     })
505     @Retention(RetentionPolicy.SOURCE)
506     public @interface AutofillCommitReason {}
507 
508     /**
509      * Autofill context was committed because of an unknown reason.
510      *
511      * @hide
512      */
513     public static final int COMMIT_REASON_UNKNOWN = 0;
514 
515     /**
516      * Autofill context was committed because activity finished.
517      *
518      * @hide
519      */
520     public static final int COMMIT_REASON_ACTIVITY_FINISHED = 1;
521 
522     /**
523      * Autofill context was committed because {@link #commit()} was called.
524      *
525      * @hide
526      */
527     public static final int COMMIT_REASON_VIEW_COMMITTED = 2;
528 
529     /**
530      * Autofill context was committed because view was clicked.
531      *
532      * @hide
533      */
534     public static final int COMMIT_REASON_VIEW_CLICKED = 3;
535 
536     /**
537      * Autofill context was committed because of view changed.
538      *
539      * @hide
540      */
541     public static final int COMMIT_REASON_VIEW_CHANGED = 4;
542     /**
543      * Autofill context was committed because of the session was destroyed.
544      *
545      * @hide
546      */
547     public static final int COMMIT_REASON_SESSION_DESTROYED = 5;
548 
549     /**
550      * Makes an authentication id from a request id and a dataset id.
551      *
552      * @param requestId The request id.
553      * @param datasetId The dataset id.
554      * @return The authentication id.
555      * @hide
556      */
makeAuthenticationId(int requestId, int datasetId)557     public static int makeAuthenticationId(int requestId, int datasetId) {
558         return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
559                 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
560     }
561 
562     /**
563      * Gets the request id from an authentication id.
564      *
565      * @param authRequestId The authentication id.
566      * @return The request id.
567      * @hide
568      */
getRequestIdFromAuthenticationId(int authRequestId)569     public static int getRequestIdFromAuthenticationId(int authRequestId) {
570         return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
571     }
572 
573     /**
574      * Gets the dataset id from an authentication id.
575      *
576      * @param authRequestId The authentication id.
577      * @return The dataset id.
578      * @hide
579      */
getDatasetIdFromAuthenticationId(int authRequestId)580     public static int getDatasetIdFromAuthenticationId(int authRequestId) {
581         return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
582     }
583 
584     private final MetricsLogger mMetricsLogger = new MetricsLogger();
585 
586     /**
587      * There is currently no session running.
588      * {@hide}
589      */
590     public static final int NO_SESSION = Integer.MAX_VALUE;
591 
592     /** @hide **/
593     public static final String PINNED_DATASET_ID = "PINNED_DATASET_ID";
594 
595     private final IAutoFillManager mService;
596 
597     private final Object mLock = new Object();
598 
599     @GuardedBy("mLock")
600     private IAutoFillManagerClient mServiceClient;
601 
602     @GuardedBy("mLock")
603     private Cleaner mServiceClientCleaner;
604 
605     @GuardedBy("mLock")
606     private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
607 
608     @GuardedBy("mLock")
609     private AutofillCallback mCallback;
610 
611     private final Context mContext;
612 
613     @GuardedBy("mLock")
614     private int mSessionId = NO_SESSION;
615 
616     @GuardedBy("mLock")
617     private int mState = STATE_UNKNOWN;
618 
619     @GuardedBy("mLock")
620     private boolean mEnabled;
621 
622     /** If a view changes to this mapping the autofill operation was successful */
623     @GuardedBy("mLock")
624     @Nullable private ParcelableMap mLastAutofilledData;
625 
626     /** If view tracking is enabled, contains the tracking state */
627     @GuardedBy("mLock")
628     @Nullable private TrackedViews mTrackedViews;
629 
630     /** Views that are only tracked because they are fillable and could be anchoring the UI. */
631     @GuardedBy("mLock")
632     @Nullable private ArraySet<AutofillId> mFillableIds;
633 
634     /** id of last requested autofill ui */
635     @Nullable private AutofillId mIdShownFillUi;
636 
637     /**
638      * Views that were already "entered" - if they're entered again when the session is not active,
639      * they're ignored
640      * */
641     @GuardedBy("mLock")
642     @Nullable private ArraySet<AutofillId> mEnteredIds;
643 
644     /**
645      * Views that were otherwised not important for autofill but triggered a session because the
646      * context is allowlisted for augmented autofill.
647      */
648     @GuardedBy("mLock")
649     @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
650 
651     /** If set, session is commited when the field is clicked. */
652     @GuardedBy("mLock")
653     @Nullable private AutofillId mSaveTriggerId;
654 
655     /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
656     @GuardedBy("mLock")
657     private boolean mOnInvisibleCalled;
658 
659     /** If set, session is commited when the activity is finished; otherwise session is canceled. */
660     @GuardedBy("mLock")
661     private boolean mSaveOnFinish;
662 
663     /** If compatibility mode is enabled - this is a bridge to interact with a11y */
664     @GuardedBy("mLock")
665     private CompatibilityBridge mCompatibilityBridge;
666 
667     @Nullable
668     private final AutofillOptions mOptions;
669 
670     /** When set, session is only used for augmented autofill requests. */
671     @GuardedBy("mLock")
672     private boolean mForAugmentedAutofillOnly;
673 
674     /**
675      * When set, standard autofill is disabled, but sessions can still be created for augmented
676      * autofill only.
677      */
678     @GuardedBy("mLock")
679     private boolean mEnabledForAugmentedAutofillOnly;
680 
681     private boolean mScreenHasCredmanField;
682 
683     /**
684      * Indicates whether there is already a field to do a fill request after
685      * the activity started.
686      *
687      * Autofill will automatically trigger a fill request after activity
688      * start if there is any field is autofillable. But if there is a field that
689      * triggered autofill, it is unnecessary to trigger again through
690      * AutofillManager#notifyViewEnteredForFillDialog.
691      */
692     private AtomicBoolean mIsFillRequested;
693 
694     @Nullable private List<AutofillId> mFillDialogTriggerIds;
695 
696     private final boolean mIsFillDialogEnabled;
697 
698     private final boolean mIsFillAndSaveDialogDisabledForCredentialManager;
699 
700     // Indicate whether trigger fill request on unimportant views is enabled
701     private boolean mIsTriggerFillRequestOnUnimportantViewEnabled = false;
702 
703     // Indicate whether to apply heuristic check on important views before trigger fill request
704     private boolean mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
705 
706     // Indicate whether to enable autofill for all view types
707     private boolean mShouldEnableAutofillOnAllViewTypes;
708 
709     // A set containing all non-autofillable ime actions passed by flag
710     private Set<String> mNonAutofillableImeActionIdSet = new ArraySet<>();
711 
712     // If a package is fully denied, then all views that marked as not
713     // important for autofill will not trigger fill request
714     private boolean mIsPackageFullyDeniedForAutofill = false;
715 
716     // If a package is partially denied, autofill manager will check whether
717     // current activity is in deny set to decide whether to trigger fill request
718     private boolean mIsPackagePartiallyDeniedForAutofill = false;
719 
720     // A deny set read from device config
721     private Set<String> mDeniedActivitySet = new ArraySet<>();
722 
723     // If a package is fully allowed, all views in package will skip the heuristic check
724     private boolean mIsPackageFullyAllowedForAutofill = false;
725 
726     // If a package is partially denied, autofill manager will check whether
727     // current activity is in allowed activity set. If it's allowed activity, then autofill manager
728     // will skip the heuristic check
729     private boolean mIsPackagePartiallyAllowedForAutofill = false;
730 
731     // An allowed activity set read from device config
732     private Set<String> mAllowedActivitySet = new ArraySet<>();
733 
734     // Whether to enable multi-line check when checking whether view is autofillable
735     private boolean mShouldEnableMultilineFilter;
736 
737     // Indicate whether should include all view with autofill type not none in assist structure
738     private boolean mShouldIncludeAllViewsWithAutofillTypeNotNoneInAssistStructure;
739 
740     // Indicate whether should include all view in assist structure
741     private boolean mShouldIncludeAllChildrenViewInAssistStructure;
742 
743     // Indicate whether WebView should always be included in the assist structure
744     private boolean mShouldAlwaysIncludeWebviewInAssistStructure;
745 
746     // Indicate whether invisibles views should be included in the assist structure
747     private boolean mShouldIncludeInvisibleViewInAssistStructure;
748 
749     // Controls logic around apps changing some properties of their views when activity loses
750     // focus due to autofill showing biometric activity, password manager, or password breach check.
751     private boolean mRelayoutFix;
752 
753     // Indicates whether the credman integration is enabled.
754     private final boolean mIsCredmanIntegrationEnabled;
755 
756     // Indicates whether called the showAutofillDialog() method.
757     private boolean mShowAutofillDialogCalled = false;
758 
759     // Cached autofill feature flag
760     private boolean mShouldIgnoreCredentialViews = false;
761 
762     private final String[] mFillDialogEnabledHints;
763 
764     // Tracked all views that have appeared, including views that there are no
765     // dataset in responses. Used to avoid request pre-fill request again and again.
766     private final ArraySet<AutofillId> mAllTrackedViews = new ArraySet<>();
767 
768     /** @hide */
769     public interface AutofillClient {
770         /**
771          * Asks the client to start an authentication flow.
772          *
773          * @param authenticationId A unique id of the authentication operation.
774          * @param intent The authentication intent.
775          * @param fillInIntent The authentication fill-in intent.
776          */
autofillClientAuthenticate(int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)777         void autofillClientAuthenticate(int authenticationId, IntentSender intent,
778                 Intent fillInIntent, boolean authenticateInline);
779 
780         /**
781          * Tells the client this manager has state to be reset.
782          */
autofillClientResetableStateAvailable()783         void autofillClientResetableStateAvailable();
784 
785         /**
786          * Request showing the autofill UI.
787          *
788          * @param anchor The real view the UI needs to anchor to.
789          * @param width The width of the fill UI content.
790          * @param height The height of the fill UI content.
791          * @param virtualBounds The bounds of the virtual decendant of the anchor.
792          * @param presenter The presenter that controls the fill UI window.
793          * @return Whether the UI was shown.
794          */
autofillClientRequestShowFillUi(@onNull View anchor, int width, int height, @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter)795         boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
796                 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
797 
798         /**
799          * Dispatch unhandled keyevent from Autofill window
800          * @param anchor The real view the UI needs to anchor to.
801          * @param keyEvent Unhandled KeyEvent from autofill window.
802          */
autofillClientDispatchUnhandledKey(@onNull View anchor, @NonNull KeyEvent keyEvent)803         void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
804 
805         /**
806          * Request hiding the autofill UI.
807          *
808          * @return Whether the UI was hidden.
809          */
autofillClientRequestHideFillUi()810         boolean autofillClientRequestHideFillUi();
811 
812         /**
813          * Gets whether the fill UI is currenlty being shown.
814          *
815          * @return Whether the fill UI is currently being shown
816          */
autofillClientIsFillUiShowing()817         boolean autofillClientIsFillUiShowing();
818 
819         /**
820          * Checks if views are currently attached and visible.
821          *
822          * @return And array with {@code true} iff the view is attached or visible
823          */
autofillClientGetViewVisibility(@onNull AutofillId[] autofillIds)824         @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
825 
826         /**
827          * Checks is the client is currently visible as understood by autofill.
828          *
829          * @return {@code true} if the client is currently visible
830          */
autofillClientIsVisibleForAutofill()831         boolean autofillClientIsVisibleForAutofill();
832 
833         /**
834          * Client might disable enter/exit event e.g. when activity is paused.
835          */
isDisablingEnterExitEventForAutofill()836         boolean isDisablingEnterExitEventForAutofill();
837 
838         /**
839          * Finds views by traversing the hierarchies of the client.
840          *
841          * @param autofillIds The autofill ids of the views to find
842          *
843          * @return And array containing the views (empty if no views found).
844          */
autofillClientFindViewsByAutofillIdTraversal( @onNull AutofillId[] autofillIds)845         @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
846                 @NonNull AutofillId[] autofillIds);
847 
848         /**
849          * Finds a view by traversing the hierarchies of the client.
850          *
851          * @param autofillId The autofill id of the views to find
852          *
853          * @return The view, or {@code null} if not found
854          */
autofillClientFindViewByAutofillIdTraversal(@onNull AutofillId autofillId)855         @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
856 
857         /**
858          * Finds a view by a11y id in a given client window.
859          *
860          * @param viewId The accessibility id of the views to find
861          * @param windowId The accessibility window id where to search
862          *
863          * @return The view, or {@code null} if not found
864          */
autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId)865         @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
866 
867         /**
868          * Runs the specified action on the UI thread.
869          */
autofillClientRunOnUiThread(Runnable action)870         void autofillClientRunOnUiThread(Runnable action);
871 
872         /**
873          * Gets the complete component name of this client.
874          */
autofillClientGetComponentName()875         ComponentName autofillClientGetComponentName();
876 
877         /**
878          * Gets the activity token
879          */
autofillClientGetActivityToken()880         @Nullable IBinder autofillClientGetActivityToken();
881 
882         /**
883           * @return Whether compatibility mode is enabled.
884           */
autofillClientIsCompatibilityModeEnabled()885         boolean autofillClientIsCompatibilityModeEnabled();
886 
887         /**
888          * Gets the next unique autofill ID.
889          *
890          * <p>Typically used to manage views whose content is recycled - see
891          * {@link View#setAutofillId(AutofillId)} for more info.
892          *
893          * @return An ID that is unique in the activity.
894          */
autofillClientGetNextAutofillId()895         @Nullable AutofillId autofillClientGetNextAutofillId();
896     }
897 
898     /**
899      * @hide
900      */
AutofillManager(Context context, IAutoFillManager service)901     public AutofillManager(Context context, IAutoFillManager service) {
902         mContext = Objects.requireNonNull(context, "context cannot be null");
903         mService = service;
904         mOptions = context.getAutofillOptions();
905         mIsFillRequested = new AtomicBoolean(false);
906 
907         mIsFillDialogEnabled = AutofillFeatureFlags.isFillDialogEnabled();
908         mFillDialogEnabledHints = AutofillFeatureFlags.getFillDialogEnabledHints();
909 
910         mIsFillAndSaveDialogDisabledForCredentialManager =
911             AutofillFeatureFlags.isFillAndSaveDialogDisabledForCredentialManager();
912 
913         if (sDebug) {
914             Log.d(TAG, "Fill dialog is enabled:" + mIsFillDialogEnabled
915                     + ", hints=" + Arrays.toString(mFillDialogEnabledHints));
916         }
917 
918         if (mOptions != null) {
919             sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
920             sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
921         }
922 
923         mIsTriggerFillRequestOnUnimportantViewEnabled =
924             AutofillFeatureFlags.isTriggerFillRequestOnUnimportantViewEnabled();
925 
926         mIsTriggerFillRequestOnFilteredImportantViewsEnabled =
927             AutofillFeatureFlags.isTriggerFillRequestOnFilteredImportantViewsEnabled();
928 
929         mShouldEnableAutofillOnAllViewTypes =
930             AutofillFeatureFlags.shouldEnableAutofillOnAllViewTypes();
931 
932         mNonAutofillableImeActionIdSet =
933             AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();
934 
935         mShouldEnableMultilineFilter =
936             AutofillFeatureFlags.shouldEnableMultilineFilter();
937 
938         final String denyListString = AutofillFeatureFlags.getDenylistStringFromFlag();
939         final String allowlistString = AutofillFeatureFlags.getAllowlistStringFromFlag();
940 
941         final String packageName = mContext.getPackageName();
942 
943         mIsPackageFullyDeniedForAutofill =
944             isPackageFullyAllowedOrDeniedForAutofill(denyListString, packageName);
945 
946         mIsPackageFullyAllowedForAutofill =
947             isPackageFullyAllowedOrDeniedForAutofill(allowlistString, packageName);
948 
949         if (!mIsPackageFullyDeniedForAutofill) {
950             mIsPackagePartiallyDeniedForAutofill =
951                 isPackagePartiallyDeniedOrAllowedForAutofill(denyListString, packageName);
952         }
953 
954         if (!mIsPackageFullyAllowedForAutofill) {
955             mIsPackagePartiallyAllowedForAutofill =
956                 isPackagePartiallyDeniedOrAllowedForAutofill(allowlistString, packageName);
957         }
958 
959         if (mIsPackagePartiallyDeniedForAutofill) {
960             mDeniedActivitySet = getDeniedOrAllowedActivitySetFromString(
961                     denyListString, packageName);
962         }
963 
964         if (mIsPackagePartiallyAllowedForAutofill) {
965             mAllowedActivitySet = getDeniedOrAllowedActivitySetFromString(
966                     allowlistString, packageName);
967         }
968 
969         mShouldIncludeAllViewsWithAutofillTypeNotNoneInAssistStructure
970             = AutofillFeatureFlags.shouldIncludeAllViewsAutofillTypeNotNoneInAssistStructrue();
971 
972         mShouldIncludeAllChildrenViewInAssistStructure
973             = AutofillFeatureFlags.shouldIncludeAllChildrenViewInAssistStructure();
974 
975         mShouldAlwaysIncludeWebviewInAssistStructure =
976                 AutofillFeatureFlags.shouldAlwaysIncludeWebviewInAssistStructure();
977 
978         mShouldIncludeInvisibleViewInAssistStructure =
979                 AutofillFeatureFlags.shouldIncludeInvisibleViewInAssistStructure();
980 
981         mRelayoutFix = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending();
982         mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
983     }
984 
985     /**
986      * Whether to apply heuristic check on important views before triggering fill request
987      *
988      * @hide
989      */
isTriggerFillRequestOnFilteredImportantViewsEnabled()990     public boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
991         return mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
992     }
993 
994     /**
995      * Whether to trigger fill request on not important views that passes heuristic check
996      *
997      * @hide
998      */
isTriggerFillRequestOnUnimportantViewEnabled()999     public boolean isTriggerFillRequestOnUnimportantViewEnabled() {
1000         return mIsTriggerFillRequestOnUnimportantViewEnabled;
1001     }
1002 
1003     /**
1004      * Whether view passes the imeAction check
1005      *
1006      */
isPassingImeActionCheck(EditText editText)1007     private boolean isPassingImeActionCheck(EditText editText) {
1008         final int actionId = editText.getImeOptions();
1009         if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
1010             Log.d(TAG, "view not autofillable - not passing ime action check");
1011             return false;
1012         }
1013         return true;
1014     }
1015 
1016     /**
1017      * Checks whether the view passed in is not multiline text
1018      *
1019      * @param editText the view that passed to this check
1020      * @return true if the view input is not multiline, false otherwise
1021      */
isPassingMultilineCheck(EditText editText)1022     private boolean isPassingMultilineCheck(EditText editText) {
1023         // check if min line is set to be greater than 1
1024         if (editText.getMinLines() > 1) {
1025             Log.d(TAG, "view not autofillable - has multiline input type");
1026             return false;
1027         }
1028         return true;
1029     }
1030 
isPackageFullyAllowedOrDeniedForAutofill( @onNull String listString, @NonNull String packageName)1031     private boolean isPackageFullyAllowedOrDeniedForAutofill(
1032             @NonNull String listString, @NonNull String packageName) {
1033         // If "PackageName:;" is in the string, then it the package is fully denied or allowed for
1034         // autofill, depending on which string is passed to this function
1035         return listString.indexOf(packageName + ":;") != -1;
1036     }
1037 
isPackagePartiallyDeniedOrAllowedForAutofill( @onNull String listString, @NonNull String packageName)1038     private boolean isPackagePartiallyDeniedOrAllowedForAutofill(
1039             @NonNull String listString, @NonNull String packageName) {
1040         // If "PackageName:" is in string when "PackageName:;" is not, then it means there are
1041         // specific activities to be allowed or denied. So the package is partially allowed or
1042         // denied for autofill.
1043         return listString.indexOf(packageName + ":") != -1;
1044     }
1045 
1046     /**
1047      * @hide
1048      */
shouldIncludeAllChildrenViewsWithAutofillTypeNotNoneInAssistStructure()1049     public boolean shouldIncludeAllChildrenViewsWithAutofillTypeNotNoneInAssistStructure()  {
1050         return mShouldIncludeAllViewsWithAutofillTypeNotNoneInAssistStructure;
1051     }
1052 
1053     /**
1054      * @hide
1055      */
shouldIncludeAllChildrenViewInAssistStructure()1056     public boolean shouldIncludeAllChildrenViewInAssistStructure() {
1057         return mShouldIncludeAllChildrenViewInAssistStructure;
1058     }
1059 
1060     /**
1061      * @hide
1062      */
shouldAlwaysIncludeWebviewInAssistStructure()1063     public boolean shouldAlwaysIncludeWebviewInAssistStructure() {
1064         return mShouldAlwaysIncludeWebviewInAssistStructure;
1065     }
1066 
1067     /**
1068      * @hide
1069      */
shouldIncludeInvisibleViewInAssistStructure()1070     public boolean shouldIncludeInvisibleViewInAssistStructure() {
1071         return mShouldIncludeInvisibleViewInAssistStructure;
1072     }
1073 
1074     /**
1075      * Get the denied or allowed activitiy names under specified package from the list string and
1076      * set it in fields accordingly
1077      *
1078      * For example, if the package name is Package1, and the string is
1079      * "Package1:Activity1,Activity2;", then the extracted activity set would be
1080      * {Activity1, Activity2}
1081      *
1082      * @param listString Denylist that is got from device config. For example,
1083      *        "Package1:Activity1,Activity2;Package2:;"
1084      * @param packageName Specify which package to extract.For example, "Package1"
1085      *
1086      * @return the extracted activity set, For example, {Activity1, Activity2}
1087      */
getDeniedOrAllowedActivitySetFromString( @onNull String listString, @NonNull String packageName)1088     private Set<String> getDeniedOrAllowedActivitySetFromString(
1089             @NonNull String listString, @NonNull String packageName) {
1090         // 1. Get the index of where the Package name starts
1091         final int packageInStringIndex = listString.indexOf(packageName + ":");
1092 
1093         // 2. Get the ";" index after this index of package
1094         final int firstNextSemicolonIndex = listString.indexOf(";", packageInStringIndex);
1095 
1096         // 3. Get the activity names substring between the indexes
1097         final int activityStringStartIndex = packageInStringIndex + packageName.length() + 1;
1098 
1099         if (activityStringStartIndex >= firstNextSemicolonIndex) {
1100             Log.e(TAG, "Failed to get denied activity names from list because it's wrongly "
1101                     + "formatted");
1102             return new ArraySet<>();
1103         }
1104         final String activitySubstring =
1105                 listString.substring(activityStringStartIndex, firstNextSemicolonIndex);
1106 
1107         // 4. Split the activity name substring
1108         final String[] activityStringArray = activitySubstring.split(",");
1109 
1110         // 5. return the extracted activities in a set
1111         return new ArraySet<>(Arrays.asList(activityStringArray));
1112     }
1113 
1114     /**
1115      * Check whether autofill is denied for current activity or package. If current activity or
1116      * package is denied, then the view won't trigger fill request.
1117      *
1118      * @hide
1119      */
isActivityDeniedForAutofill()1120     public boolean isActivityDeniedForAutofill() {
1121         if (mIsPackageFullyDeniedForAutofill) {
1122             return true;
1123         }
1124         if (mIsPackagePartiallyDeniedForAutofill) {
1125             final AutofillClient client = getClient();
1126             if (client == null) {
1127                 return false;
1128             }
1129             final ComponentName clientActivity = client.autofillClientGetComponentName();
1130             if (mDeniedActivitySet.contains(clientActivity.flattenToShortString())) {
1131                 return true;
1132             }
1133         }
1134         return false;
1135     }
1136 
1137     /**
1138      * Check whether current activity is allowlisted for autofill.
1139      *
1140      * If it is, the view in current activity will bypass heuristic check when checking whether it's
1141      * autofillable
1142      *
1143      * @hide
1144      */
isActivityAllowedForAutofill()1145     public boolean isActivityAllowedForAutofill() {
1146         if (mIsPackageFullyAllowedForAutofill) {
1147             return true;
1148         }
1149         if (mIsPackagePartiallyAllowedForAutofill) {
1150             final AutofillClient client = getClient();
1151             if (client == null) {
1152                 return false;
1153             }
1154             final ComponentName clientActivity = client.autofillClientGetComponentName();
1155             if (mAllowedActivitySet.contains(clientActivity.flattenToShortString())) {
1156                 return true;
1157             }
1158         }
1159         return false;
1160     }
1161 
1162     /**
1163      * Check heuristics and other rules to determine if view is autofillable
1164      *
1165      * Note: this function should be only called only when autofill for all apps is turned on. The
1166      * calling method needs to check the corresponding flag to make sure that before calling into
1167      * this function.
1168      *
1169      * @hide
1170      */
isAutofillable(View view)1171     public boolean isAutofillable(View view) {
1172         // Duplicate the autofill type check here because ViewGroup will call this function to
1173         // decide whether to include view in assist structure.
1174         // Also keep the autofill type check inside View#IsAutofillable() to serve as an early out
1175         // or if other functions need to call it.
1176         if (view.getAutofillType() == View.AUTOFILL_TYPE_NONE) return false;
1177 
1178         // denylist only applies to not important views
1179         if (!view.isImportantForAutofill() && isActivityDeniedForAutofill()) {
1180             return false;
1181         }
1182 
1183         if (isActivityAllowedForAutofill()) {
1184             return true;
1185         }
1186 
1187         if (view instanceof EditText) {
1188             if (mShouldEnableMultilineFilter && !isPassingMultilineCheck((EditText) view)) {
1189                 return false;
1190             }
1191             return isPassingImeActionCheck((EditText) view);
1192         }
1193 
1194         // Skip view type check if view is important for autofill or
1195         // shouldEnableAutofillOnAllViewTypes flag is turned on
1196         if (view.isImportantForAutofill() || mShouldEnableAutofillOnAllViewTypes) {
1197             return true;
1198         }
1199 
1200         if (view instanceof CheckBox
1201             || view instanceof Spinner
1202             || view instanceof DatePicker
1203             || view instanceof TimePicker
1204             || view instanceof RadioGroup) {
1205             return true;
1206         }
1207         Log.d(TAG, "view is not autofillable - not important and filtered by view type check");
1208         return false;
1209     }
1210 
1211     /**
1212      * @hide
1213      */
enableCompatibilityMode()1214     public void enableCompatibilityMode() {
1215         synchronized (mLock) {
1216             // The accessibility manager is a singleton so we may need to plug
1217             // different bridge based on which activity is currently focused
1218             // in the current process. Since compat would be rarely used, just
1219             // create and register a new instance every time.
1220             if (sDebug) {
1221                 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
1222             }
1223             mCompatibilityBridge = new CompatibilityBridge();
1224         }
1225     }
1226 
1227     /**
1228      * Restore state after activity lifecycle
1229      *
1230      * @param savedInstanceState The state to be restored
1231      *
1232      * {@hide}
1233      */
onCreate(Bundle savedInstanceState)1234     public void onCreate(Bundle savedInstanceState) {
1235         if (!hasAutofillFeature()) {
1236             return;
1237         }
1238         synchronized (mLock) {
1239             mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG, android.view.autofill.ParcelableMap.class);
1240 
1241             if (isActiveLocked()) {
1242                 Log.w(TAG, "New session was started before onCreate()");
1243                 return;
1244             }
1245 
1246             mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
1247             mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
1248 
1249             if (mSessionId != NO_SESSION) {
1250                 final boolean clientAdded = tryAddServiceClientIfNeededLocked();
1251 
1252                 final AutofillClient client = getClient();
1253                 if (client != null) {
1254                     final SyncResultReceiver receiver = new SyncResultReceiver(
1255                             SYNC_CALLS_TIMEOUT_MS);
1256                     try {
1257                         boolean sessionWasRestored = false;
1258                         if (clientAdded) {
1259                             mService.restoreSession(mSessionId,
1260                                     client.autofillClientGetActivityToken(),
1261                                     mServiceClient.asBinder(), receiver);
1262                             sessionWasRestored = receiver.getIntResult() == 1;
1263                         } else {
1264                             Log.w(TAG, "No service client for session " + mSessionId);
1265                         }
1266 
1267                         if (!sessionWasRestored) {
1268                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
1269                             mSessionId = NO_SESSION;
1270                             mState = STATE_UNKNOWN;
1271                         } else {
1272                             if (sDebug) {
1273                                 Log.d(TAG, "session " + mSessionId + " was restored");
1274                             }
1275 
1276                             client.autofillClientResetableStateAvailable();
1277                         }
1278                     } catch (RemoteException e) {
1279                         Log.e(TAG, "Could not figure out if there was an autofill session", e);
1280                     } catch (SyncResultReceiver.TimeoutException e) {
1281                         Log.e(TAG, "Fail to get session restore status: " + e);
1282                     }
1283                 }
1284             }
1285         }
1286     }
1287 
1288     /**
1289      * Called once the client becomes visible.
1290      *
1291      * @see AutofillClient#autofillClientIsVisibleForAutofill()
1292      *
1293      * {@hide}
1294      */
onVisibleForAutofill()1295     public void onVisibleForAutofill() {
1296         // This gets called when the client just got visible at which point the visibility
1297         // of the tracked views may not have been computed (due to a pending layout, etc).
1298         // While generally we have no way to know when the UI has settled. We will evaluate
1299         // the tracked views state at the end of next frame to guarantee that everything
1300         // that may need to be laid out is laid out.
1301         Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
1302             synchronized (mLock) {
1303                 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
1304                     mTrackedViews.onVisibleForAutofillChangedLocked();
1305                 }
1306             }
1307         }, null);
1308     }
1309 
1310     /**
1311      * Called once the client becomes invisible.
1312      *
1313      * @see AutofillClient#autofillClientIsVisibleForAutofill()
1314      *
1315      * @param isExpiredResponse The response has expired or not
1316      *
1317      * {@hide}
1318      */
onInvisibleForAutofill(boolean isExpiredResponse)1319     public void onInvisibleForAutofill(boolean isExpiredResponse) {
1320         synchronized (mLock) {
1321             mOnInvisibleCalled = true;
1322 
1323             if (isExpiredResponse) {
1324                 // Notify service the response has expired.
1325                 updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
1326                         ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
1327             }
1328         }
1329     }
1330 
1331     /**
1332      * Save state before activity lifecycle
1333      *
1334      * @param outState Place to store the state
1335      *
1336      * {@hide}
1337      */
onSaveInstanceState(Bundle outState)1338     public void onSaveInstanceState(Bundle outState) {
1339         if (!hasAutofillFeature()) {
1340             return;
1341         }
1342         synchronized (mLock) {
1343             if (mSessionId != NO_SESSION) {
1344                 outState.putInt(SESSION_ID_TAG, mSessionId);
1345             }
1346             if (mState != STATE_UNKNOWN) {
1347                 outState.putInt(STATE_TAG, mState);
1348             }
1349             if (mLastAutofilledData != null) {
1350                 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
1351             }
1352         }
1353     }
1354 
1355     /**
1356      * @hide
1357      */
1358     @GuardedBy("mLock")
isCompatibilityModeEnabledLocked()1359     public boolean isCompatibilityModeEnabledLocked() {
1360         return mCompatibilityBridge != null;
1361     }
1362 
1363     /**
1364      * Checks whether autofill is enabled for the current user.
1365      *
1366      * <p>Typically used to determine whether the option to explicitly request autofill should
1367      * be offered - see {@link #requestAutofill(View)}.
1368      *
1369      * @return whether autofill is enabled for the current user.
1370      */
isEnabled()1371     public boolean isEnabled() {
1372         if (!hasAutofillFeature()) {
1373             return false;
1374         }
1375         synchronized (mLock) {
1376             if (isDisabledByServiceLocked()) {
1377                 return false;
1378             }
1379             final boolean clientAdded = tryAddServiceClientIfNeededLocked();
1380             return clientAdded ? mEnabled : false;
1381         }
1382     }
1383 
1384     /**
1385      * Should always be called from {@link AutofillService#getFillEventHistory()}.
1386      *
1387      * @hide
1388      */
getFillEventHistory()1389     @Nullable public FillEventHistory getFillEventHistory() {
1390         try {
1391             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1392             mService.getFillEventHistory(receiver);
1393             return receiver.getParcelableResult();
1394         } catch (RemoteException e) {
1395             throw e.rethrowFromSystemServer();
1396         } catch (SyncResultReceiver.TimeoutException e) {
1397             Log.e(TAG, "Fail to get fill event history: " + e);
1398             return null;
1399         }
1400     }
1401 
1402     /**
1403      * Explicitly requests a new autofill context.
1404      *
1405      * <p>Normally, the autofill context is automatically started if necessary when
1406      * {@link #notifyViewEntered(View)} is called, but this method should be used in the
1407      * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
1408      * option on its contextual overflow menu, and the user selects it.
1409      *
1410      * @param view view requesting the new autofill context.
1411      */
requestAutofill(@onNull View view)1412     public void requestAutofill(@NonNull View view) {
1413         int flags = FLAG_MANUAL_REQUEST;
1414         if (!view.isFocused()) {
1415             flags |= FLAG_VIEW_NOT_FOCUSED;
1416         }
1417         notifyViewEntered(view, flags);
1418     }
1419 
1420     /**
1421      * Explicitly cancels the current session and requests a new autofill context.
1422      *
1423      * <p>Normally, the autofill context is automatically started if necessary when
1424      * {@link #notifyViewEntered(View)} is called, but this method should be used in
1425      * cases where it must be explicitly started or restarted. Currently, this method should only
1426      * be called by
1427      * {@link android.service.autofill.augmented.AugmentedAutofillService#requestAutofill(
1428      * ComponentName, AutofillId)} to cancel the current session and trigger the autofill flow in
1429      * a new session, giving the autofill service or the augmented autofill service a chance to
1430      * send updated suggestions.
1431      *
1432      * @param view view requesting the new autofill context.
1433      */
requestAutofillFromNewSession(@onNull View view)1434     void requestAutofillFromNewSession(@NonNull View view) {
1435         cancel();
1436         notifyViewEntered(view);
1437     }
1438 
1439     /**
1440      * Explicitly requests a new autofill context for virtual views.
1441      *
1442      * <p>Normally, the autofill context is automatically started if necessary when
1443      * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
1444      * cases where it must be explicitly started. For example, when the virtual view offers an
1445      * AUTOFILL option on its contextual overflow menu, and the user selects it.
1446      *
1447      * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1448      * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
1449      * the absolute bounds could be calculated by:
1450      *
1451      * <pre class="prettyprint">
1452      *   int offset[] = new int[2];
1453      *   getLocationOnScreen(offset);
1454      *   Rect absBounds = new Rect(bounds.left + offset[0],
1455      *       bounds.top + offset[1],
1456      *       bounds.right + offset[0], bounds.bottom + offset[1]);
1457      * </pre>
1458      *
1459      * @param view the virtual view parent.
1460      * @param virtualId id identifying the virtual child inside the parent view.
1461      * @param absBounds absolute boundaries of the virtual view in the screen.
1462      */
requestAutofill(@onNull View view, int virtualId, @NonNull Rect absBounds)1463     public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1464         int flags = FLAG_MANUAL_REQUEST;
1465         if (!view.isFocused()) {
1466             flags |= FLAG_VIEW_NOT_FOCUSED;
1467         }
1468         notifyViewEntered(view, virtualId, absBounds, flags);
1469     }
1470 
1471     /**
1472      * Called when a {@link View} that supports autofill is entered.
1473      *
1474      * @param view {@link View} that was entered.
1475      */
notifyViewEntered(@onNull View view)1476     public void notifyViewEntered(@NonNull View view) {
1477         notifyViewEntered(view, 0);
1478     }
1479 
1480     /**
1481      * Called when the virtual views are ready to the user for autofill.
1482      *
1483      * This method is used to notify autofill system the views are ready to the user. And then
1484      * Autofill can do initialization if needed before the user starts to input. For example, do
1485      * a pre-fill request for the
1486      * <a href="/reference/android/service/autofill/Dataset.html#FillDialogUI">fill dialog</a>.
1487      *
1488      * @param view the host view that holds a virtual view hierarchy.
1489      * @param infos extra information for the virtual views. The key is virtual id which represents
1490      *             the virtual view in the host view.
1491      *
1492      * @throws IllegalArgumentException if the {@code infos} was empty
1493      */
notifyVirtualViewsReady( @onNull View view, @NonNull SparseArray<VirtualViewFillInfo> infos)1494     public void notifyVirtualViewsReady(
1495             @NonNull View view, @NonNull SparseArray<VirtualViewFillInfo> infos) {
1496         Objects.requireNonNull(infos);
1497         if (infos.size() == 0) {
1498             throw new IllegalArgumentException("No VirtualViewInfo found");
1499         }
1500         boolean isCredmanRequested = false;
1501         if (shouldSuppressDialogsForCredman(view)
1502                 && mIsFillAndSaveDialogDisabledForCredentialManager) {
1503             mScreenHasCredmanField = true;
1504             if (isCredmanRequested(view)) {
1505                 if (sDebug) {
1506                     Log.d(TAG, "Prefetching fill response for credMan: "
1507                             + view.getAutofillId().toString());
1508                 }
1509                 isCredmanRequested = true;
1510             } else {
1511                 if (sDebug) {
1512                     Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
1513                             + view.getAutofillId().toString());
1514                 }
1515                 return;
1516             }
1517         }
1518         for (int i = 0; i < infos.size(); i++) {
1519             final VirtualViewFillInfo info = infos.valueAt(i);
1520             final int virtualId = infos.keyAt(i);
1521             notifyViewReadyInner(getAutofillId(view, virtualId),
1522                     (info == null) ? null : info.getAutofillHints(), isCredmanRequested);
1523         }
1524     }
1525 
1526     /**
1527      * The {@link AutofillFeatureFlags#DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED} is {@code true} or
1528      * the view have the allowed autofill hints, performs a fill request to know there is any field
1529      * supported fill dialog.
1530      *
1531      * @hide
1532      */
notifyViewEnteredForFillDialog(View v)1533     public void notifyViewEnteredForFillDialog(View v) {
1534         boolean isCredmanRequested = false;
1535         if (shouldSuppressDialogsForCredman(v)
1536                 && mIsFillAndSaveDialogDisabledForCredentialManager) {
1537             mScreenHasCredmanField = true;
1538             if (isCredmanRequested(v)) {
1539                 if (sDebug) {
1540                     Log.d(TAG, "Prefetching fill response for credMan: "
1541                             + v.getAutofillId().toString());
1542                 }
1543                 isCredmanRequested = true;
1544             } else {
1545                 if (sDebug) {
1546                     Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
1547                             + v.getAutofillId().toString());
1548                 }
1549                 return;
1550             }
1551         }
1552         notifyViewReadyInner(v.getAutofillId(), v.getAutofillHints(), isCredmanRequested);
1553     }
1554 
notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints, boolean isCredmanRequested)1555     private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints,
1556             boolean isCredmanRequested) {
1557         if (sDebug) {
1558             Log.d(TAG, "notifyViewReadyInner:" + id);
1559         }
1560 
1561         if (!hasAutofillFeature()) {
1562             return;
1563         }
1564         synchronized (mLock) {
1565             if (mAllTrackedViews.contains(id)) {
1566                 // The id is tracked and will not trigger pre-fill request again.
1567                 return;
1568             }
1569 
1570             // Add the id as tracked to avoid triggering fill request again and again.
1571             mAllTrackedViews.add(id);
1572             if (mTrackedViews != null) {
1573                 // To support the fill dialog can show for the autofillable Views in
1574                 // different pages but in the same Activity. We need to reset the
1575                 // mIsFillRequested flag to allow asking for a new FillRequest when
1576                 // user switches to other page
1577                 mTrackedViews.checkViewState(id);
1578             }
1579         }
1580 
1581         // Skip if the fill request has been performed for a view.
1582         if (mIsFillRequested.get()) {
1583             return;
1584         }
1585 
1586         // Start session with PCC flag to get assist structure and send field classification request
1587         // to PCC classification service.
1588         if (AutofillFeatureFlags.isAutofillPccClassificationEnabled()) {
1589             synchronized (mLock) {
1590                 // If session has already been created, that'd mean we already have issued the
1591                 // detection request previously. It is possible in cases like autofocus that this
1592                 // method isn't invoked, so the server should still handle such cases where fill
1593                 // request comes in but PCC Detection hasn't been triggered. There is no benefit to
1594                 // trigger PCC Detection separately in those cases.
1595                 if (!isActiveLocked()) {
1596                     final boolean clientAdded =
1597                             tryAddServiceClientIfNeededLocked(isCredmanRequested);
1598                     if (clientAdded) {
1599                         startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, /* bounds= */ null,
1600                             /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
1601                     } else {
1602                         if (sVerbose) {
1603                             Log.v(TAG, "not starting session: no service client");
1604                         }
1605                     }
1606                 }
1607             }
1608         }
1609 
1610         // Check if framework should send pre-fill request for fill dialog
1611         boolean shouldSendPreFillRequestForFillDialog = false;
1612         if (mIsFillDialogEnabled) {
1613             shouldSendPreFillRequestForFillDialog = true;
1614         } else if (autofillHints != null) {
1615             // check if supported autofill hint is present
1616             for (String autofillHint : autofillHints) {
1617                 for (String filldialogEnabledHint : mFillDialogEnabledHints) {
1618                     if (filldialogEnabledHint.equalsIgnoreCase(autofillHint)) {
1619                         shouldSendPreFillRequestForFillDialog = true;
1620                         break;
1621                     }
1622                 }
1623                 if (shouldSendPreFillRequestForFillDialog) break;
1624             }
1625         }
1626         if (shouldSendPreFillRequestForFillDialog) {
1627             if (sDebug) {
1628                 Log.d(TAG, "Triggering pre-emptive request for fill dialog.");
1629             }
1630             int flags = FLAG_SUPPORTS_FILL_DIALOG;
1631             flags |= FLAG_VIEW_NOT_FOCUSED;
1632             if (isCredmanRequested) {
1633                 if (sDebug) {
1634                     Log.d(TAG, "Pre fill request is triggered for credMan");
1635                 }
1636                 flags |= FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
1637             }
1638             synchronized (mLock) {
1639                 // To match the id of the IME served view, used AutofillId.NO_AUTOFILL_ID on prefill
1640                 // request, because IME will reset the id of IME served view to 0 when activity
1641                 // start and does not focus on any view. If the id of the prefill request does
1642                 // not match the IME served view's, Autofill will be blocking to wait inline
1643                 // request from the IME.
1644                 notifyViewEnteredLocked(/* view= */ null, AutofillId.NO_AUTOFILL_ID,
1645                     /* bounds= */ null,  /* value= */ null, flags);
1646             }
1647         }
1648         return;
1649     }
1650 
hasFillDialogUiFeature()1651     private boolean hasFillDialogUiFeature() {
1652         return mIsFillDialogEnabled || !ArrayUtils.isEmpty(mFillDialogEnabledHints);
1653     }
1654 
getImeStateFlag(View v)1655     private int getImeStateFlag(View v) {
1656         if (v == null) return 0;
1657 
1658         final WindowInsets rootWindowInsets = v.getRootWindowInsets();
1659         if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsets.Type.ime())) {
1660             return FLAG_IME_SHOWING;
1661         }
1662         return 0;
1663     }
1664 
1665     @GuardedBy("mLock")
shouldIgnoreViewEnteredLocked(@onNull AutofillId id, int flags)1666     private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
1667         if (isDisabledByServiceLocked()) {
1668             if (sVerbose) {
1669                 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
1670                         + ") on state " + getStateAsStringLocked() + " because disabled by svc");
1671             }
1672             return true;
1673         }
1674         if (isFinishedLocked()) {
1675             // Session already finished: ignore if automatic request and view already entered
1676             if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
1677                     && mEnteredIds.contains(id)) {
1678                 if (sVerbose) {
1679                     Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
1680                             + ") on state " + getStateAsStringLocked()
1681                             + " because view was already entered: " + mEnteredIds);
1682                 }
1683                 return true;
1684             }
1685         }
1686         return false;
1687     }
1688 
isClientVisibleForAutofillLocked()1689     private boolean isClientVisibleForAutofillLocked() {
1690         final AutofillClient client = getClient();
1691         return client != null && client.autofillClientIsVisibleForAutofill();
1692     }
1693 
isClientDisablingEnterExitEvent()1694     private boolean isClientDisablingEnterExitEvent() {
1695         final AutofillClient client = getClient();
1696         return client != null && client.isDisablingEnterExitEventForAutofill();
1697     }
1698 
notifyViewEntered(@onNull View view, int flags)1699     private void notifyViewEntered(@NonNull View view, int flags) {
1700         if (!hasAutofillFeature()) {
1701             return;
1702         }
1703         AutofillCallback callback;
1704         synchronized (mLock) {
1705             callback = notifyViewEnteredLocked(
1706                     view, view.getAutofillId(), /* bounds= */ null, view.getAutofillValue(), flags);
1707         }
1708 
1709         if (callback != null) {
1710             mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1711         }
1712     }
1713 
1714     /**
1715      * Called when a {@link View} that supports autofill is exited.
1716      *
1717      * @param view {@link View} that was exited.
1718      */
notifyViewExited(@onNull View view)1719     public void notifyViewExited(@NonNull View view) {
1720         if (!hasAutofillFeature()) {
1721             return;
1722         }
1723         synchronized (mLock) {
1724             notifyViewExitedLocked(view);
1725         }
1726     }
1727 
1728     @GuardedBy("mLock")
notifyViewExitedLocked(@onNull View view)1729     void notifyViewExitedLocked(@NonNull View view) {
1730         final boolean clientAdded = tryAddServiceClientIfNeededLocked();
1731 
1732         if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
1733                 && isActiveLocked()) {
1734             // dont notify exited when Activity is already in background
1735             if (!isClientDisablingEnterExitEvent()) {
1736                 final AutofillId id = view.getAutofillId();
1737 
1738                 // Update focus on existing session.
1739                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
1740             }
1741         }
1742     }
1743 
1744     /**
1745      * Called when a {@link View view's} visibility changed.
1746      *
1747      * @param view {@link View} that was exited.
1748      * @param isVisible visible if the view is visible in the view hierarchy.
1749      */
notifyViewVisibilityChanged(@onNull View view, boolean isVisible)1750     public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1751         notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1752     }
1753 
1754     /**
1755      * Called when a virtual view's visibility changed.
1756      *
1757      * @param view {@link View} that was exited.
1758      * @param virtualId id identifying the virtual child inside the parent view.
1759      * @param isVisible visible if the view is visible in the view hierarchy.
1760      */
notifyViewVisibilityChanged(@onNull View view, int virtualId, boolean isVisible)1761     public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1762         notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1763     }
1764 
1765     /**
1766      * Called when a view/virtual view's visibility changed.
1767      *
1768      * @param view {@link View} that was exited.
1769      * @param virtualId id identifying the virtual child inside the parent view.
1770      * @param isVisible visible if the view is visible in the view hierarchy.
1771      * @param virtual Whether the view is virtual.
1772      */
notifyViewVisibilityChangedInternal(@onNull View view, int virtualId, boolean isVisible, boolean virtual)1773     private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1774             boolean isVisible, boolean virtual) {
1775         synchronized (mLock) {
1776             if (mForAugmentedAutofillOnly) {
1777                 if (sVerbose) {
1778                     Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
1779                 }
1780                 return;
1781             }
1782             if (mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION) {
1783                 if (sVerbose) {
1784                     Log.v(TAG, "notifyViewVisibilityChanged(): ignoring in auth pending mode");
1785                 }
1786                 return;
1787             }
1788             if (mEnabled && isActiveLocked()) {
1789                 final AutofillId id = virtual ? getAutofillId(view, virtualId)
1790                         : view.getAutofillId();
1791                 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
1792                 if (!isVisible && mFillableIds != null) {
1793                     if (mFillableIds.contains(id)) {
1794                         if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1795                         requestHideFillUi(id, view);
1796                     }
1797                 }
1798                 if (mTrackedViews != null) {
1799                     mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
1800                 } else if (sVerbose) {
1801                     Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
1802                 }
1803             } else if (!virtual && isVisible) {
1804                 startAutofillIfNeededLocked(view);
1805             }
1806         }
1807     }
1808 
1809     /**
1810      * Called when a virtual view that supports autofill is entered.
1811      *
1812      * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1813      * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1814      * the absolute bounds could be calculated by:
1815      *
1816      * <pre class="prettyprint">
1817      *   int offset[] = new int[2];
1818      *   getLocationOnScreen(offset);
1819      *   Rect absBounds = new Rect(bounds.left + offset[0],
1820      *       bounds.top + offset[1],
1821      *       bounds.right + offset[0], bounds.bottom + offset[1]);
1822      * </pre>
1823      *
1824      * @param view the virtual view parent.
1825      * @param virtualId id identifying the virtual child inside the parent view.
1826      * @param absBounds absolute boundaries of the virtual view in the screen.
1827      */
notifyViewEntered(@onNull View view, int virtualId, @NonNull Rect absBounds)1828     public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1829         notifyViewEntered(view, virtualId, absBounds, 0);
1830     }
1831 
notifyViewEntered(View view, int virtualId, Rect bounds, int flags)1832     private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
1833         if (!hasAutofillFeature()) {
1834             return;
1835         }
1836 
1837         AutofillCallback callback;
1838         synchronized (mLock) {
1839             callback = notifyViewEnteredLocked(
1840                     view, getAutofillId(view, virtualId), bounds, /* value= */ null, flags);
1841         }
1842 
1843         if (callback != null) {
1844             callback.onAutofillEvent(view, virtualId,
1845                     AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1846         }
1847     }
1848 
1849     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
1850     @GuardedBy("mLock")
notifyViewEnteredLocked(@ullable View view, AutofillId id, Rect bounds, AutofillValue value, int flags)1851     private AutofillCallback notifyViewEnteredLocked(@Nullable View view, AutofillId id,
1852             Rect bounds, AutofillValue value, int flags) {
1853         if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
1854 
1855         boolean credmanRequested = isCredmanRequested(view);
1856         final boolean clientAdded = tryAddServiceClientIfNeededLocked(credmanRequested);
1857         if (!clientAdded) {
1858             if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
1859             return null;
1860         }
1861 
1862         if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
1863             if (sVerbose) {
1864                 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1865             }
1866             return mCallback;
1867         }
1868         if (mIsCredmanIntegrationEnabled && isCredmanRequested(view)) {
1869             flags |= FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
1870         }
1871         mIsFillRequested.set(true);
1872 
1873         // don't notify entered when Activity is already in background
1874         if (!isClientDisablingEnterExitEvent()) {
1875             if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1876                 flags |= FLAG_PASSWORD_INPUT_TYPE;
1877             }
1878 
1879             // Update session when screen has credman field
1880             if (AutofillFeatureFlags.isFillAndSaveDialogDisabledForCredentialManager()
1881                     && mScreenHasCredmanField) {
1882                 flags |= FLAG_SCREEN_HAS_CREDMAN_FIELD;
1883                 if (sVerbose) {
1884                     Log.v(TAG, "updating session with flag screen has credman view");
1885                 }
1886             }
1887 
1888             flags |= getImeStateFlag(view);
1889 
1890             if (!isActiveLocked()) {
1891                 // Starts new session.
1892                 startSessionLocked(id, bounds, value, flags);
1893             } else {
1894                 // Update focus on existing session.
1895                 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1896                     if (sDebug) {
1897                         Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1898                                 + "mForAugmentedAutofillOnly on manual request");
1899                     }
1900                     mForAugmentedAutofillOnly = false;
1901                 }
1902 
1903                 if ((flags & FLAG_SUPPORTS_FILL_DIALOG) != 0) {
1904                     flags |= FLAG_RESET_FILL_DIALOG_STATE;
1905                 }
1906 
1907                 updateSessionLocked(id, bounds, value, ACTION_VIEW_ENTERED, flags);
1908             }
1909             addEnteredIdLocked(id);
1910         }
1911         return null;
1912     }
1913 
1914     @GuardedBy("mLock")
addEnteredIdLocked(@onNull AutofillId id)1915     private void addEnteredIdLocked(@NonNull AutofillId id) {
1916         if (mEnteredIds == null) {
1917             mEnteredIds = new ArraySet<>(1);
1918         }
1919         id.resetSessionId();
1920         mEnteredIds.add(id);
1921     }
1922 
1923     /**
1924      * Called when a virtual view that supports autofill is exited.
1925      *
1926      * @param view the virtual view parent.
1927      * @param virtualId id identifying the virtual child inside the parent view.
1928      */
notifyViewExited(@onNull View view, int virtualId)1929     public void notifyViewExited(@NonNull View view, int virtualId) {
1930         if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
1931         if (!hasAutofillFeature()) {
1932             return;
1933         }
1934         synchronized (mLock) {
1935             notifyViewExitedLocked(view, virtualId);
1936         }
1937     }
1938 
1939     @GuardedBy("mLock")
notifyViewExitedLocked(@onNull View view, int virtualId)1940     private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1941         final boolean clientAdded = tryAddServiceClientIfNeededLocked();
1942 
1943         if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
1944                 && isActiveLocked()) {
1945             // don't notify exited when Activity is already in background
1946             if (!isClientDisablingEnterExitEvent()) {
1947                 final AutofillId id = getAutofillId(view, virtualId);
1948 
1949                 // Update focus on existing session.
1950                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
1951             }
1952         }
1953     }
1954 
1955     /**
1956      * Called to indicate the value of an autofillable {@link View} changed.
1957      *
1958      * @param view view whose value changed.
1959      */
notifyValueChanged(View view)1960     public void notifyValueChanged(View view) {
1961         if (!hasAutofillFeature()) {
1962             return;
1963         }
1964         AutofillId id = null;
1965         boolean valueWasRead = false;
1966         AutofillValue value = null;
1967 
1968         synchronized (mLock) {
1969             // If the session is gone some fields might still be highlighted, hence we have to
1970             // remove the isAutofilled property even if no sessions are active.
1971             if (mLastAutofilledData == null) {
1972                 view.setAutofilled(false, false);
1973             } else {
1974                 id = view.getAutofillId();
1975                 if (mLastAutofilledData.containsKey(id)) {
1976                     value = view.getAutofillValue();
1977                     valueWasRead = true;
1978                     final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1;
1979 
1980                     if (Objects.equals(mLastAutofilledData.get(id), value)) {
1981                         view.setAutofilled(true, hideHighlight);
1982                         try {
1983                             mService.setViewAutofilled(mSessionId, id, mContext.getUserId());
1984                         } catch (RemoteException e) {
1985                             // The failure could be a consequence of something going wrong on the
1986                             // server side. Do nothing here since it's just logging, but it's
1987                             // possible follow-up actions may fail.
1988                         }
1989                     } else {
1990                         view.setAutofilled(false, false);
1991                         mLastAutofilledData.remove(id);
1992                     }
1993                 } else {
1994                     view.setAutofilled(false, false);
1995                 }
1996             }
1997 
1998             if (!mEnabled || !isActiveLocked()) {
1999                 if (!startAutofillIfNeededLocked(view)) {
2000                     if (sVerbose) {
2001                         Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
2002                                 + "): ignoring on state " + getStateAsStringLocked());
2003                     }
2004                 }
2005                 return;
2006             }
2007 
2008             if (id == null) {
2009                 id = view.getAutofillId();
2010             }
2011 
2012             if (!valueWasRead) {
2013                 value = view.getAutofillValue();
2014             }
2015 
2016             updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, getImeStateFlag(view));
2017         }
2018     }
2019 
2020     /**
2021      * Called to indicate the value of an autofillable virtual view has changed.
2022      *
2023      * @param view the virtual view parent.
2024      * @param virtualId id identifying the virtual child inside the parent view.
2025      * @param value new value of the child.
2026      */
notifyValueChanged(View view, int virtualId, AutofillValue value)2027     public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
2028         if (!hasAutofillFeature()) {
2029             return;
2030         }
2031         if (DBG) {
2032             Log.v(TAG, "notifyValueChanged() called with virtualId:" + virtualId + " value:"
2033                     + value);
2034         }
2035         synchronized (mLock) {
2036             if (mLastAutofilledData != null) {
2037                 AutofillId id = new AutofillId(view.getAutofillId(), virtualId, mSessionId);
2038                 if (mLastAutofilledData.containsKey(id)) {
2039                     if (Objects.equals(mLastAutofilledData.get(id), value)) {
2040                         // Indicates that the view was autofilled
2041                         if (sDebug) {
2042                             Log.v(TAG, "notifyValueChanged() virtual view autofilled successfully:"
2043                                     + virtualId + " value:" + value);
2044                         }
2045                         try {
2046                             mService.setViewAutofilled(mSessionId, id, mContext.getUserId());
2047                         } catch (RemoteException e) {
2048                             // The failure could be a consequence of something going wrong on the
2049                             // server side. Do nothing here since it's just logging, but it's
2050                             // possible follow-up actions may fail.
2051                             Log.w(TAG, "RemoteException caught but ignored " + e);
2052                         }
2053                     }
2054                 }
2055             }
2056             if (!mEnabled || !isActiveLocked()) {
2057                 if (sVerbose) {
2058                     Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
2059                             + "): ignoring on state " + getStateAsStringLocked());
2060                 }
2061                 return;
2062             }
2063 
2064             final AutofillId id = getAutofillId(view, virtualId);
2065             updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, getImeStateFlag(view));
2066         }
2067     }
2068 
2069     /**
2070      * Called to indicate a {@link View} is clicked.
2071      *
2072      * @param view view that has been clicked.
2073      */
notifyViewClicked(@onNull View view)2074     public void notifyViewClicked(@NonNull View view) {
2075         notifyViewClicked(view.getAutofillId());
2076     }
2077 
2078     /**
2079      * Called to indicate a virtual view has been clicked.
2080      *
2081      * @param view the virtual view parent.
2082      * @param virtualId id identifying the virtual child inside the parent view.
2083      */
notifyViewClicked(@onNull View view, int virtualId)2084     public void notifyViewClicked(@NonNull View view, int virtualId) {
2085         notifyViewClicked(getAutofillId(view, virtualId));
2086     }
2087 
notifyViewClicked(AutofillId id)2088     private void notifyViewClicked(AutofillId id) {
2089         if (!hasAutofillFeature()) {
2090             return;
2091         }
2092         if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
2093 
2094         synchronized (mLock) {
2095             if (!mEnabled || !isActiveLocked()) {
2096                 return;
2097             }
2098             if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
2099                 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
2100                 commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_CLICKED);
2101                 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
2102             }
2103         }
2104     }
2105 
2106     /**
2107      * Called by {@link android.app.Activity} to commit or cancel the session on finish.
2108      *
2109      * @hide
2110      */
onActivityFinishing()2111     public void onActivityFinishing() {
2112         if (!hasAutofillFeature()) {
2113             return;
2114         }
2115         synchronized (mLock) {
2116             if (mSaveOnFinish) {
2117                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
2118                 commitLocked(/* commitReason= */ COMMIT_REASON_ACTIVITY_FINISHED);
2119             } else {
2120                 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
2121                 cancelLocked();
2122             }
2123         }
2124     }
2125 
2126     /**
2127      * Called to indicate the current autofill context should be commited.
2128      *
2129      * <p>This method is typically called by {@link View Views} that manage virtual views; for
2130      * example, when the view is rendering an {@code HTML} page with a form and virtual views
2131      * that represent the HTML elements, it should call this method after the form is submitted and
2132      * another page is rendered.
2133      *
2134      * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
2135      * methods such as {@link android.app.Activity#finish()}.
2136      */
commit()2137     public void commit() {
2138         if (!hasAutofillFeature()) {
2139             return;
2140         }
2141         if (sVerbose) Log.v(TAG, "commit() called by app");
2142         synchronized (mLock) {
2143             commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_COMMITTED);
2144         }
2145     }
2146 
2147     @GuardedBy("mLock")
commitLocked(@utofillCommitReason int commitReason)2148     private void commitLocked(@AutofillCommitReason int commitReason) {
2149         if (!mEnabled && !isActiveLocked()) {
2150             return;
2151         }
2152         finishSessionLocked(/* commitReason= */ commitReason);
2153     }
2154 
2155     /**
2156      * Called to indicate the current autofill context should be cancelled.
2157      *
2158      * <p>This method is typically called by {@link View Views} that manage virtual views; for
2159      * example, when the view is rendering an {@code HTML} page with a form and virtual views
2160      * that represent the HTML elements, it should call this method if the user does not post the
2161      * form but moves to another form in this page.
2162      *
2163      * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
2164      * methods such as {@link android.app.Activity#finish()}.
2165      */
cancel()2166     public void cancel() {
2167         if (sVerbose) Log.v(TAG, "cancel() called by app or augmented autofill service");
2168         if (!hasAutofillFeature()) {
2169             return;
2170         }
2171         synchronized (mLock) {
2172             cancelLocked();
2173         }
2174     }
2175 
2176     @GuardedBy("mLock")
cancelLocked()2177     private void cancelLocked() {
2178         if (!mEnabled && !isActiveLocked()) {
2179             return;
2180         }
2181         cancelSessionLocked();
2182     }
2183 
2184     /** @hide */
disableOwnedAutofillServices()2185     public void disableOwnedAutofillServices() {
2186         disableAutofillServices();
2187     }
2188 
2189     /**
2190      * If the app calling this API has enabled autofill services they
2191      * will be disabled.
2192      */
disableAutofillServices()2193     public void disableAutofillServices() {
2194         if (!hasAutofillFeature()) {
2195             return;
2196         }
2197         try {
2198             mService.disableOwnedAutofillServices(mContext.getUserId());
2199         } catch (RemoteException e) {
2200             throw e.rethrowFromSystemServer();
2201         }
2202     }
2203 
2204     /**
2205      * Returns {@code true} if the calling application provides a {@link AutofillService} that is
2206      * enabled for the current user, or {@code false} otherwise.
2207      */
hasEnabledAutofillServices()2208     public boolean hasEnabledAutofillServices() {
2209         if (mService == null) return false;
2210 
2211         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2212         try {
2213             mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(),
2214                     receiver);
2215             return receiver.getIntResult() == 1;
2216         } catch (RemoteException e) {
2217             throw e.rethrowFromSystemServer();
2218         } catch (SyncResultReceiver.TimeoutException e) {
2219             throw new RuntimeException("Fail to get enabled autofill services status. " + e);
2220         }
2221     }
2222 
2223     /**
2224      * Returns the component name of the {@link AutofillService} that is enabled for the current
2225      * user.
2226      */
2227     @Nullable
getAutofillServiceComponentName()2228     public ComponentName getAutofillServiceComponentName() {
2229         if (mService == null) return null;
2230 
2231         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2232         try {
2233             mService.getAutofillServiceComponentName(receiver);
2234             return receiver.getParcelableResult();
2235         } catch (RemoteException e) {
2236             throw e.rethrowFromSystemServer();
2237         } catch (SyncResultReceiver.TimeoutException e) {
2238             throw new RuntimeException("Fail to get autofill services component name. " + e);
2239         }
2240     }
2241 
2242     /**
2243      * Gets the id of the {@link UserData} used for
2244      * <a href="AutofillService.html#FieldClassification">field classification</a>.
2245      *
2246      * <p>This method is useful when the service must check the status of the {@link UserData} in
2247      * the device without fetching the whole object.
2248      *
2249      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2250      * and it's ignored if the caller currently doesn't have an enabled autofill service for
2251      * the user.
2252      *
2253      * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
2254      * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
2255      * service for the user.
2256      */
getUserDataId()2257     @Nullable public String getUserDataId() {
2258         try {
2259             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2260             mService.getUserDataId(receiver);
2261             return receiver.getStringResult();
2262         } catch (RemoteException e) {
2263             throw e.rethrowFromSystemServer();
2264         } catch (SyncResultReceiver.TimeoutException e) {
2265             throw new RuntimeException("Fail to get user data id for field classification. " + e);
2266         }
2267     }
2268 
2269     /**
2270      * Gets the user data used for
2271      * <a href="AutofillService.html#FieldClassification">field classification</a>.
2272      *
2273      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2274      * and it's ignored if the caller currently doesn't have an enabled autofill service for
2275      * the user.
2276      *
2277      * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
2278      * reset or if the caller currently does not have an enabled autofill service for the user.
2279      */
getUserData()2280     @Nullable public UserData getUserData() {
2281         try {
2282             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2283             mService.getUserData(receiver);
2284             return receiver.getParcelableResult();
2285         } catch (RemoteException e) {
2286             throw e.rethrowFromSystemServer();
2287         } catch (SyncResultReceiver.TimeoutException e) {
2288             throw new RuntimeException("Fail to get user data for field classification. " + e);
2289         }
2290     }
2291 
2292     /**
2293      * Sets the {@link UserData} used for
2294      * <a href="AutofillService.html#FieldClassification">field classification</a>
2295      *
2296      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2297      * and it's ignored if the caller currently doesn't have an enabled autofill service for
2298      * the user.
2299      */
setUserData(@ullable UserData userData)2300     public void setUserData(@Nullable UserData userData) {
2301         try {
2302             mService.setUserData(userData);
2303         } catch (RemoteException e) {
2304             throw e.rethrowFromSystemServer();
2305         }
2306     }
2307 
2308     /**
2309      * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
2310      * enabled.
2311      *
2312      * <p>As field classification is an expensive operation, it could be disabled, either
2313      * temporarily (for example, because the service exceeded a rate-limit threshold) or
2314      * permanently (for example, because the device is a low-level device).
2315      *
2316      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2317      * and it's ignored if the caller currently doesn't have an enabled autofill service for
2318      * the user.
2319      */
isFieldClassificationEnabled()2320     public boolean isFieldClassificationEnabled() {
2321         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2322         try {
2323             mService.isFieldClassificationEnabled(receiver);
2324             return receiver.getIntResult() == 1;
2325         } catch (RemoteException e) {
2326             throw e.rethrowFromSystemServer();
2327         } catch (SyncResultReceiver.TimeoutException e) {
2328             throw new RuntimeException("Fail to get field classification enabled status. " + e);
2329         }
2330     }
2331 
2332     /**
2333      * Gets the name of the default algorithm used for
2334      * <a href="AutofillService.html#FieldClassification">field classification</a>.
2335      *
2336      * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
2337      * set.
2338      *
2339      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2340      * and it's ignored if the caller currently doesn't have an enabled autofill service for
2341      * the user.
2342      */
2343     @Nullable
getDefaultFieldClassificationAlgorithm()2344     public String getDefaultFieldClassificationAlgorithm() {
2345         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2346         try {
2347             mService.getDefaultFieldClassificationAlgorithm(receiver);
2348             return receiver.getStringResult();
2349         } catch (RemoteException e) {
2350             throw e.rethrowFromSystemServer();
2351         } catch (SyncResultReceiver.TimeoutException e) {
2352             throw new RuntimeException("Fail to get default field classification algorithm. " + e);
2353         }
2354     }
2355 
2356     /**
2357      * Gets the name of all algorithms currently available for
2358      * <a href="AutofillService.html#FieldClassification">field classification</a>.
2359      *
2360      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
2361      * and it returns an empty list if the caller currently doesn't have an enabled autofill service
2362      * for the user.
2363      */
2364     @NonNull
getAvailableFieldClassificationAlgorithms()2365     public List<String> getAvailableFieldClassificationAlgorithms() {
2366         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2367         try {
2368             mService.getAvailableFieldClassificationAlgorithms(receiver);
2369             final String[] algorithms = receiver.getStringArrayResult();
2370             return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
2371         } catch (RemoteException e) {
2372             throw e.rethrowFromSystemServer();
2373         } catch (SyncResultReceiver.TimeoutException e) {
2374             throw new
2375                 RuntimeException("Fail to get available field classification algorithms. " + e);
2376         }
2377     }
2378 
2379     /**
2380      * Returns {@code true} if autofill is supported by the current device and
2381      * is supported for this user.
2382      *
2383      * <p>Autofill is typically supported, but it could be unsupported in cases like:
2384      * <ol>
2385      *     <li>Low-end devices.
2386      *     <li>Device policy rules that forbid its usage.
2387      * </ol>
2388      */
isAutofillSupported()2389     public boolean isAutofillSupported() {
2390         if (mService == null) return false;
2391 
2392         final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2393         try {
2394             mService.isServiceSupported(mContext.getUserId(), receiver);
2395             return receiver.getIntResult() == 1;
2396         } catch (RemoteException e) {
2397             throw e.rethrowFromSystemServer();
2398         } catch (SyncResultReceiver.TimeoutException e) {
2399             throw new RuntimeException("Fail to get autofill supported status. " + e);
2400         }
2401     }
2402 
2403     // Note: don't need to use locked suffix because mContext is final.
getClient()2404     private AutofillClient getClient() {
2405         final AutofillClient client = mContext.getAutofillClient();
2406         if (client == null && sVerbose) {
2407             Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
2408                     + mContext);
2409         }
2410         return client;
2411     }
2412 
2413     /**
2414      * Check if autofill ui is showing, must be called on UI thread.
2415      * @hide
2416      */
isAutofillUiShowing()2417     public boolean isAutofillUiShowing() {
2418         final AutofillClient client = mContext.getAutofillClient();
2419         return client != null && client.autofillClientIsFillUiShowing();
2420     }
2421 
2422     /** @hide */
shouldIgnoreCredentialViews()2423     public boolean shouldIgnoreCredentialViews() {
2424         return mShouldIgnoreCredentialViews;
2425     }
2426 
2427     /** @hide */
onAuthenticationResult(int authenticationId, Intent data, View focusView)2428     public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
2429         if (!hasAutofillFeature()) {
2430             return;
2431         }
2432         // TODO: the result code is being ignored, so this method is not reliably
2433         // handling the cases where it's not RESULT_OK: it works fine if the service does not
2434         // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
2435         // service set the extra and returned RESULT_CANCELED...
2436 
2437         if (sDebug) {
2438             Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
2439         }
2440 
2441         synchronized (mLock) {
2442             if (!isActiveLocked()) {
2443                 Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
2444                 return;
2445             }
2446             mState = STATE_ACTIVE;
2447             // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
2448             // of app activity.  We enforce enter event to re-show fill ui in such case.
2449             // CTS example:
2450             //     LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
2451             //     LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
2452             if (!mOnInvisibleCalled && focusView != null
2453                     && focusView.canNotifyAutofillEnterExitEvent()) {
2454                 notifyViewExitedLocked(focusView);
2455                 notifyViewEnteredLocked(focusView, focusView.getAutofillId(),
2456                         /* bounds= */ null, focusView.getAutofillValue(), /* flags= */ 0);
2457             }
2458             if (data == null) {
2459                 // data is set to null when result is not RESULT_OK
2460                 Log.i(TAG, "onAuthenticationResult(): empty intent");
2461                 return;
2462             }
2463 
2464             final Parcelable result;
2465             if (data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT) != null) {
2466                 result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
2467             } else if (data.getParcelableExtra(
2468                     CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE) != null
2469                     && Flags.autofillCredmanIntegration()) {
2470                 result = data.getParcelableExtra(
2471                         CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE);
2472             } else {
2473                 result = null;
2474             }
2475 
2476             final Bundle responseData = new Bundle();
2477             responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
2478             Serializable exception = data.getSerializableExtra(
2479                     CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
2480                     GetCredentialException.class);
2481             if (exception != null && Flags.autofillCredmanIntegration()) {
2482                 responseData.putSerializable(
2483                         CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, exception);
2484             }
2485             final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
2486             if (newClientState != null) {
2487                 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
2488             }
2489             if (data.hasExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
2490                 responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
2491                         data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
2492                                 false));
2493             }
2494             try {
2495                 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
2496                         mContext.getUserId());
2497             } catch (RemoteException e) {
2498                 Log.e(TAG, "Error delivering authentication result", e);
2499             }
2500         }
2501     }
2502 
2503     /**
2504      * Gets the next unique autofill ID for the activity context.
2505      *
2506      * <p>Typically used to manage views whose content is recycled - see
2507      * {@link View#setAutofillId(AutofillId)} for more info.
2508      *
2509      * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
2510      * the {@link Context} associated with this {@link AutofillManager}.
2511      */
2512     @Nullable
getNextAutofillId()2513     public AutofillId getNextAutofillId() {
2514         final AutofillClient client = getClient();
2515         if (client == null) return null;
2516 
2517         final AutofillId id = client.autofillClientGetNextAutofillId();
2518 
2519         if (id == null && sDebug) {
2520             Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
2521         }
2522 
2523         return id;
2524     }
2525 
getAutofillId(View parent, int virtualId)2526     private static AutofillId getAutofillId(View parent, int virtualId) {
2527         return new AutofillId(parent.getAutofillViewId(), virtualId);
2528     }
2529 
2530     @GuardedBy("mLock")
startSessionLocked(@onNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags)2531     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
2532             @NonNull AutofillValue value, int flags) {
2533         if (mEnteredForAugmentedAutofillIds != null
2534                 && mEnteredForAugmentedAutofillIds.contains(id)
2535                 || mEnabledForAugmentedAutofillOnly) {
2536             if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
2537             flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
2538         }
2539         if (sVerbose) {
2540             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
2541                     + ", flags=" + flags + ", state=" + getStateAsStringLocked()
2542                     + ", compatMode=" + isCompatibilityModeEnabledLocked()
2543                     + ", augmentedOnly=" + mForAugmentedAutofillOnly
2544                     + ", enabledAugmentedOnly=" + mEnabledForAugmentedAutofillOnly
2545                     + ", enteredIds=" + mEnteredIds);
2546         }
2547         // We need to reset the augmented-only state when a manual request is made, as it's possible
2548         // that the service returned null for the first request and now the user is manually
2549         // requesting autofill to trigger a custom UI provided by the service.
2550         if (mForAugmentedAutofillOnly && !mEnabledForAugmentedAutofillOnly
2551                 && (flags & FLAG_MANUAL_REQUEST) != 0) {
2552             if (sVerbose) {
2553                 Log.v(TAG, "resetting mForAugmentedAutofillOnly on manual autofill request");
2554             }
2555             mForAugmentedAutofillOnly = false;
2556         }
2557         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
2558             if (sVerbose) {
2559                 Log.v(TAG, "not automatically starting session for " + id
2560                         + " on state " + getStateAsStringLocked() + " and flags " + flags);
2561             }
2562             return;
2563         }
2564         try {
2565             final AutofillClient client = getClient();
2566             if (client == null) return; // NOTE: getClient() already logged it..
2567 
2568             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2569             final ComponentName clientActivity = client.autofillClientGetComponentName();
2570 
2571             if (!mEnabledForAugmentedAutofillOnly && mOptions != null
2572                     && mOptions.isAutofillDisabledLocked(clientActivity)) {
2573                 if (mOptions.isAugmentedAutofillEnabled(mContext)) {
2574                     if (sDebug) {
2575                         Log.d(TAG, "startSession(" + clientActivity + "): disabled by service but "
2576                                 + "allowlisted for augmented autofill");
2577                         flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
2578                     }
2579                 } else {
2580                     if (sDebug) {
2581                         Log.d(TAG, "startSession(" + clientActivity + "): ignored because "
2582                                 + "disabled by service and not allowlisted for augmented autofill");
2583                     }
2584                     setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
2585                     client.autofillClientResetableStateAvailable();
2586                     return;
2587                 }
2588             }
2589 
2590             mService.startSession(client.autofillClientGetActivityToken(),
2591                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
2592                     mCallback != null, flags, clientActivity,
2593                     isCompatibilityModeEnabledLocked(), receiver);
2594             mSessionId = receiver.getIntResult();
2595             if (mSessionId != NO_SESSION) {
2596                 mState = STATE_ACTIVE;
2597             }
2598             final int extraFlags = receiver.getOptionalExtraIntResult(0);
2599             if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
2600                 if (sDebug) Log.d(TAG, "startSession(" + clientActivity + "): for augmented only");
2601                 mForAugmentedAutofillOnly = true;
2602             }
2603             client.autofillClientResetableStateAvailable();
2604         } catch (RemoteException e) {
2605             throw e.rethrowFromSystemServer();
2606         } catch (SyncResultReceiver.TimeoutException e) {
2607             // no-op, just log the error message.
2608             Log.w(TAG, "Exception getting result from SyncResultReceiver: " + e);
2609         }
2610     }
2611 
2612     @GuardedBy("mLock")
finishSessionLocked(@utofillCommitReason int commitReason)2613     private void finishSessionLocked(@AutofillCommitReason int commitReason) {
2614         if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
2615 
2616         if (!isActiveLocked()) return;
2617 
2618         try {
2619             mService.finishSession(mSessionId, mContext.getUserId(), commitReason);
2620         } catch (RemoteException e) {
2621             throw e.rethrowFromSystemServer();
2622         }
2623 
2624         resetSessionLocked(/* resetEnteredIds= */ true);
2625     }
2626 
2627     @GuardedBy("mLock")
cancelSessionLocked()2628     private void cancelSessionLocked() {
2629         if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
2630 
2631         if (!isActiveLocked()) return;
2632 
2633         try {
2634             mService.cancelSession(mSessionId, mContext.getUserId());
2635         } catch (RemoteException e) {
2636             throw e.rethrowFromSystemServer();
2637         }
2638 
2639         resetSessionLocked(/* resetEnteredIds= */ true);
2640     }
2641 
2642     @GuardedBy("mLock")
resetSessionLocked(boolean resetEnteredIds)2643     private void resetSessionLocked(boolean resetEnteredIds) {
2644         mSessionId = NO_SESSION;
2645         mState = STATE_UNKNOWN;
2646         mTrackedViews = null;
2647         mFillableIds = null;
2648         mSaveTriggerId = null;
2649         mIdShownFillUi = null;
2650         mIsFillRequested.set(false);
2651         mShowAutofillDialogCalled = false;
2652         mFillDialogTriggerIds = null;
2653         mScreenHasCredmanField = false;
2654         mAllTrackedViews.clear();
2655         if (resetEnteredIds) {
2656             mEnteredIds = null;
2657         }
2658     }
2659 
2660     @GuardedBy("mLock")
updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, int flags)2661     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
2662             int flags) {
2663         if (sVerbose) {
2664             Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
2665                     + ", value=" + value + ", action=" + action + ", flags=" + flags);
2666         }
2667         try {
2668             mService.updateSession(mSessionId, id, bounds, value, action, flags,
2669                     mContext.getUserId());
2670         } catch (RemoteException e) {
2671             throw e.rethrowFromSystemServer();
2672         }
2673     }
2674 
2675     /**
2676      * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
2677      * if the AutofillManagerClient is added successfully or is already added. Otherwise,
2678      * returns {@code false}.
2679      */
2680     @GuardedBy("mLock")
tryAddServiceClientIfNeededLocked()2681     private boolean tryAddServiceClientIfNeededLocked() {
2682         return tryAddServiceClientIfNeededLocked(/*credmanRequested=*/ false);
2683     }
2684 
2685     @GuardedBy("mLock")
tryAddServiceClientIfNeededLocked(boolean credmanRequested)2686     private boolean tryAddServiceClientIfNeededLocked(boolean credmanRequested) {
2687         final AutofillClient client = getClient();
2688         if (client == null) {
2689             return false;
2690         }
2691         if (mService == null) {
2692             Log.w(TAG, "Autofill service is null!");
2693             return false;
2694         }
2695         if (mServiceClient == null) {
2696             mServiceClient = new AutofillManagerClient(this);
2697             try {
2698                 final int userId = mContext.getUserId();
2699                 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2700                 mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
2701                         userId, receiver, credmanRequested);
2702                 int flags = 0;
2703                 try {
2704                     flags = receiver.getIntResult();
2705                 } catch (SyncResultReceiver.TimeoutException e) {
2706                     Log.w(TAG, "Failed to initialize autofill: " + e);
2707                     // Reset the states initialized above.
2708                     mService.removeClient(mServiceClient, userId);
2709                     mServiceClient = null;
2710                     return false;
2711                 }
2712                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
2713                 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
2714                 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
2715                 mEnabledForAugmentedAutofillOnly = (flags
2716                         & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
2717                 if (sVerbose) {
2718                     Log.v(TAG, "receiver results: flags=" + flags + " enabled=" + mEnabled
2719                             + ", enabledForAugmentedOnly: " + mEnabledForAugmentedAutofillOnly);
2720                 }
2721                 final IAutoFillManager service = mService;
2722                 final IAutoFillManagerClient serviceClient = mServiceClient;
2723                 mServiceClientCleaner = Cleaner.create(this, () -> {
2724                     // TODO(b/123100811): call service to also remove reference to
2725                     // mAugmentedAutofillServiceClient
2726                     try {
2727                         service.removeClient(serviceClient, userId);
2728                     } catch (RemoteException e) {
2729                     }
2730                 });
2731             } catch (RemoteException e) {
2732                 throw e.rethrowFromSystemServer();
2733             }
2734         }
2735         return true;
2736     }
2737 
2738     @GuardedBy("mLock")
startAutofillIfNeededLocked(View view)2739     private boolean startAutofillIfNeededLocked(View view) {
2740         if (mState == STATE_UNKNOWN
2741                 && mSessionId == NO_SESSION
2742                 && view instanceof EditText
2743                 && !TextUtils.isEmpty(((EditText) view).getText())
2744                 && !view.isFocused()
2745                 && view.isImportantForAutofill()
2746                 && view.isLaidOut()
2747                 && view.isVisibleToUser()) {
2748 
2749             final boolean clientAdded = tryAddServiceClientIfNeededLocked();
2750 
2751             if (sVerbose) {
2752                 Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
2753                         + mServiceClient);
2754             }
2755             if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
2756                 final AutofillId id = view.getAutofillId();
2757                 final AutofillValue value = view.getAutofillValue();
2758                 // Starts new session.
2759                 startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
2760                 // Updates value.
2761                 updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
2762                         /* flags= */ 0);
2763                 addEnteredIdLocked(id);
2764                 return true;
2765             }
2766         }
2767         return false;
2768     }
2769 
2770     /**
2771      * Registers a {@link AutofillCallback} to receive autofill events.
2772      *
2773      * @param callback callback to receive events.
2774      */
registerCallback(@ullable AutofillCallback callback)2775     public void registerCallback(@Nullable AutofillCallback callback) {
2776         if (!hasAutofillFeature()) {
2777             return;
2778         }
2779         synchronized (mLock) {
2780             if (callback == null) return;
2781 
2782             final boolean hadCallback = mCallback != null;
2783             mCallback = callback;
2784 
2785             if (!hadCallback) {
2786                 try {
2787                     mService.setHasCallback(mSessionId, mContext.getUserId(), true);
2788                 } catch (RemoteException e) {
2789                     throw e.rethrowFromSystemServer();
2790                 }
2791             }
2792         }
2793     }
2794 
2795     /**
2796      * Unregisters a {@link AutofillCallback} to receive autofill events.
2797      *
2798      * @param callback callback to stop receiving events.
2799      */
unregisterCallback(@ullable AutofillCallback callback)2800     public void unregisterCallback(@Nullable AutofillCallback callback) {
2801         if (!hasAutofillFeature()) {
2802             return;
2803         }
2804         synchronized (mLock) {
2805             if (callback == null || mCallback == null || callback != mCallback) return;
2806 
2807             mCallback = null;
2808 
2809             try {
2810                 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
2811             } catch (RemoteException e) {
2812                 throw e.rethrowFromSystemServer();
2813             }
2814         }
2815     }
2816 
2817     /**
2818      * Explicitly limits augmented autofill to the given packages and activities.
2819      *
2820      * <p>To reset the allowlist, call it passing {@code null} to both arguments.
2821      *
2822      * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
2823      * apps that uses addresses. For example, if the service wants to support augmented autofill on
2824      * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
2825      * of {@code AddressApp2}, it would call:
2826      * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
2827      * Arrays.asList(new ComponentName("AddressApp2", "act1"),
2828      * new ComponentName("AddressApp2", "act2")));}
2829      *
2830      * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
2831      * service, and it's ignored if the caller isn't it.
2832      *
2833      * @hide
2834      */
2835     @SystemApi
setAugmentedAutofillWhitelist(@ullable Set<String> packages, @Nullable Set<ComponentName> activities)2836     public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
2837             @Nullable Set<ComponentName> activities) {
2838         if (!hasAutofillFeature()) {
2839             return;
2840         }
2841 
2842         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2843         int resultCode;
2844         try {
2845             mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
2846                     resultReceiver);
2847             resultCode = resultReceiver.getIntResult();
2848         } catch (RemoteException e) {
2849             throw e.rethrowFromSystemServer();
2850         } catch (SyncResultReceiver.TimeoutException e) {
2851             Log.e(TAG, "Fail to get the result of set AugmentedAutofill whitelist. " + e);
2852             return;
2853         }
2854         switch (resultCode) {
2855             case RESULT_OK:
2856                 return;
2857             case RESULT_CODE_NOT_SERVICE:
2858                 throw new SecurityException("caller is not user's Augmented Autofill Service");
2859             default:
2860                 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
2861                         + resultCode);
2862         }
2863     }
2864 
2865     /**
2866      * Notifies that a non-autofillable view was entered because the activity is allowlisted for
2867      * augmented autofill.
2868      *
2869      * <p>This method is necessary to set the right flag on start, so the server-side session
2870      * doesn't trigger the standard autofill workflow, but the augmented's instead.
2871      *
2872      * @hide
2873      */
notifyViewEnteredForAugmentedAutofill(@onNull View view)2874     public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
2875         final AutofillId id = view.getAutofillId();
2876         synchronized (mLock) {
2877             if (mEnteredForAugmentedAutofillIds == null) {
2878                 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
2879             }
2880             mEnteredForAugmentedAutofillIds.add(id);
2881         }
2882     }
2883 
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)2884     private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2885             Rect anchorBounds, IAutofillWindowPresenter presenter) {
2886         final View anchor = findView(id);
2887         if (anchor == null) {
2888             return;
2889         }
2890 
2891         AutofillCallback callback = null;
2892         synchronized (mLock) {
2893             if (mSessionId == sessionId) {
2894                 AutofillClient client = getClient();
2895 
2896                 if (client != null) {
2897                     if (client.autofillClientRequestShowFillUi(anchor, width, height,
2898                             anchorBounds, presenter)) {
2899                         callback = mCallback;
2900                         mIdShownFillUi = id;
2901                     }
2902                 }
2903             }
2904         }
2905 
2906         if (callback != null) {
2907             if (id.isVirtualInt()) {
2908                 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
2909                         AutofillCallback.EVENT_INPUT_SHOWN);
2910             } else {
2911                 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
2912             }
2913         }
2914     }
2915 
authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)2916     private void authenticate(int sessionId, int authenticationId, IntentSender intent,
2917             Intent fillInIntent, boolean authenticateInline) {
2918         synchronized (mLock) {
2919             if (sessionId == mSessionId) {
2920                 if (mRelayoutFix) {
2921                     mState = STATE_PENDING_AUTHENTICATION;
2922                 }
2923                 final AutofillClient client = getClient();
2924                 if (client != null) {
2925                     // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
2926                     // before onAuthenticationResult()
2927                     mOnInvisibleCalled = false;
2928                     client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
2929                             authenticateInline);
2930                 }
2931             }
2932         }
2933     }
2934 
dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent)2935     private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
2936         final View anchor = findView(id);
2937         if (anchor == null) {
2938             return;
2939         }
2940 
2941         synchronized (mLock) {
2942             if (mSessionId == sessionId) {
2943                 AutofillClient client = getClient();
2944 
2945                 if (client != null) {
2946                     client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
2947                 }
2948             }
2949         }
2950     }
2951 
2952     /** @hide */
2953     public static final int SET_STATE_FLAG_ENABLED = 0x01;
2954     /** @hide */
2955     public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
2956     /** @hide */
2957     public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
2958     /** @hide */
2959     public static final int SET_STATE_FLAG_DEBUG = 0x08;
2960     /** @hide */
2961     public static final int SET_STATE_FLAG_VERBOSE = 0x10;
2962     /** @hide */
2963     public static final int SET_STATE_FLAG_FOR_AUTOFILL_ONLY = 0x20;
2964 
setState(int flags)2965     private void setState(int flags) {
2966         if (sVerbose) {
2967             Log.v(TAG, "setState(" + flags + ": " + DebugUtils.flagsToString(AutofillManager.class,
2968                     "SET_STATE_FLAG_", flags) + ")");
2969         }
2970         synchronized (mLock) {
2971             if ((flags & SET_STATE_FLAG_FOR_AUTOFILL_ONLY) != 0) {
2972                 mForAugmentedAutofillOnly = true;
2973                 // NOTE: returning right away as this is the only flag set, at least currently...
2974                 return;
2975             }
2976             mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
2977             if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
2978                 // Reset the session state
2979                 resetSessionLocked(/* resetEnteredIds= */ true);
2980             }
2981             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
2982                 // Reset connection to system
2983                 mServiceClient = null;
2984                 mAugmentedAutofillServiceClient = null;
2985                 if (mServiceClientCleaner != null) {
2986                     mServiceClientCleaner.clean();
2987                     mServiceClientCleaner = null;
2988                 }
2989                 notifyReenableAutofill();
2990             }
2991         }
2992         sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2993         sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
2994     }
2995 
2996     /**
2997      * Sets a view as autofilled if the current value is the {code targetValue}.
2998      *
2999      * @param view The view that is to be autofilled
3000      * @param targetValue The value we want to fill into view
3001      */
setAutofilledIfValuesIs(@onNull View view, @Nullable AutofillValue targetValue, boolean hideHighlight)3002     private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
3003             boolean hideHighlight) {
3004         AutofillValue currentValue = view.getAutofillValue();
3005         if (Objects.equals(currentValue, targetValue)) {
3006             synchronized (mLock) {
3007                 if (mLastAutofilledData == null) {
3008                     mLastAutofilledData = new ParcelableMap(1);
3009                 }
3010                 mLastAutofilledData.put(view.getAutofillId(), targetValue);
3011             }
3012             view.setAutofilled(true, hideHighlight);
3013             if (sDebug) {
3014                 Log.d(TAG, "View " + view.getAutofillId() + " autofilled synchronously.");
3015             }
3016             try {
3017                 mService.setViewAutofilled(mSessionId, view.getAutofillId(), mContext.getUserId());
3018             } catch (RemoteException e) {
3019                 // The failure could be a consequence of something going wrong on the server side.
3020                 // Do nothing here since it's just logging, but it's possible follow-up actions may
3021                 // fail.
3022                 Log.w(TAG, "Unable to log due to " + e);
3023             }
3024         } else {
3025             if (sDebug) {
3026                 Log.d(TAG, "View " + view.getAutofillId() + " " + view.getClass().toString()
3027                         + " from " + view.getClass().getPackageName()
3028                         + " : didn't fill in synchronously. It may fill asynchronously.");
3029             }
3030         }
3031     }
3032 
3033     /**
3034      * Returns String with text "null" if the object is null, or the actual string represented by
3035      * the object.
3036      */
getString(Object obj)3037     private @NonNull String getString(Object obj) {
3038         return obj == null ? "null" : obj.toString();
3039     }
3040 
onGetCredentialException(int sessionId, AutofillId id, String errorType, String errorMsg)3041     private void onGetCredentialException(int sessionId, AutofillId id, String errorType,
3042             String errorMsg) {
3043         synchronized (mLock) {
3044             if (sessionId != mSessionId) {
3045                 Log.w(TAG, "onGetCredentialException afm sessionIds don't match");
3046                 return;
3047             }
3048 
3049             final AutofillClient client = getClient();
3050             if (client == null) {
3051                 Log.w(TAG, "onGetCredentialException afm client id null");
3052                 return;
3053             }
3054             ArrayList<AutofillId> failedIds = new ArrayList<>();
3055             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
3056                     Helper.toArray(new ArrayList<>(Collections.singleton(id))));
3057             if (views == null || views.length == 0) {
3058                 Log.w(TAG, "onGetCredentialException afm client view not found");
3059                 return;
3060             }
3061 
3062             final View view = views[0];
3063             if (view == null) {
3064                 Log.i(TAG, "onGetCredentialException View is null");
3065 
3066                 // Most likely view has been removed after the initial request was sent to the
3067                 // the service; this is fine, but we need to update the view status in the
3068                 // server side so it can be triggered again.
3069                 Log.d(TAG, "onGetCredentialException(): no View with id " + id);
3070                 failedIds.add(id);
3071             }
3072             if (id.isVirtualInt()) {
3073                 Log.i(TAG, "onGetCredentialException afm client id is virtual");
3074                 // TODO(b/326314286): Handle virtual views
3075             } else {
3076                 Log.i(TAG, "onGetCredentialException afm client id is NOT virtual");
3077                 view.onGetCredentialException(errorType, errorMsg);
3078             }
3079             handleFailedIdsLocked(failedIds);
3080         }
3081     }
3082 
onGetCredentialResponse(int sessionId, AutofillId id, GetCredentialResponse response)3083     private void onGetCredentialResponse(int sessionId, AutofillId id,
3084             GetCredentialResponse response) {
3085         synchronized (mLock) {
3086             if (sessionId != mSessionId) {
3087                 Log.w(TAG, "onGetCredentialResponse afm sessionIds don't match");
3088                 return;
3089             }
3090 
3091             final AutofillClient client = getClient();
3092             if (client == null) {
3093                 Log.w(TAG, "onGetCredentialResponse afm client id null");
3094                 return;
3095             }
3096             ArrayList<AutofillId> failedIds = new ArrayList<>();
3097             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
3098                     Helper.toArray(new ArrayList<>(Collections.singleton(id))));
3099             if (views == null || views.length == 0) {
3100                 Log.w(TAG, "onGetCredentialResponse afm client view not found");
3101                 return;
3102             }
3103 
3104             final View view = views[0];
3105             if (view == null) {
3106                 Log.i(TAG, "onGetCredentialResponse View is null");
3107 
3108                 // Most likely view has been removed after the initial request was sent to the
3109                 // the service; this is fine, but we need to update the view status in the
3110                 // server side so it can be triggered again.
3111                 Log.d(TAG, "onGetCredentialResponse(): no View with id " + id);
3112                 failedIds.add(id);
3113             }
3114             if (id.isVirtualInt()) {
3115                 Log.i(TAG, "onGetCredentialResponse afm client id is virtual");
3116                 // TODO(b/326314286): Handle virtual views
3117             } else {
3118                 Log.i(TAG, "onGetCredentialResponse afm client id is NOT virtual");
3119                 view.onGetCredentialResponse(response);
3120             }
3121             handleFailedIdsLocked(failedIds);
3122         }
3123     }
3124 
3125     @GuardedBy("mLock")
handleFailedIdsLocked(@onNull ArrayList<AutofillId> failedIds)3126     private void handleFailedIdsLocked(@NonNull ArrayList<AutofillId> failedIds) {
3127         if (!failedIds.isEmpty() && sVerbose) {
3128             Log.v(TAG, "autofill(): total failed views: " + failedIds);
3129         }
3130         try {
3131             mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
3132         } catch (RemoteException e) {
3133             // In theory, we could ignore this error since it's not a big deal, but
3134             // in reality, we rather crash the app anyways, as the failure could be
3135             // a consequence of something going wrong on the server side...
3136             throw e.rethrowFromSystemServer();
3137         }
3138     }
3139 
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)3140     private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3141             boolean hideHighlight) {
3142         synchronized (mLock) {
3143             if (sessionId != mSessionId) {
3144                 return;
3145             }
3146 
3147             final AutofillClient client = getClient();
3148             if (client == null) {
3149                 return;
3150             }
3151 
3152             final int itemCount = ids.size();
3153             int numApplied = 0;
3154             ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
3155             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
3156                     Helper.toArray(ids));
3157 
3158             ArrayList<AutofillId> failedIds = new ArrayList<>();
3159 
3160             if (mLastAutofilledData == null) {
3161                 mLastAutofilledData = new ParcelableMap(itemCount);
3162             }
3163 
3164             for (int i = 0; i < itemCount; i++) {
3165                 final AutofillId id = ids.get(i);
3166                 final AutofillValue value = values.get(i);
3167                 final View view = views[i];
3168                 if (view == null) {
3169                     // Most likely view has been removed after the initial request was sent to the
3170                     // the service; this is fine, but we need to update the view status in the
3171                     // server side so it can be triggered again.
3172                     Log.d(TAG, "autofill(): no View with id " + id);
3173                     failedIds.add(id);
3174                     continue;
3175                 }
3176                 // Mark the view as to be autofilled with 'value'
3177                 mLastAutofilledData.put(id, value);
3178 
3179                 if (id.isVirtualInt()) {
3180                     if (virtualValues == null) {
3181                         // Most likely there will be just one view with virtual children.
3182                         virtualValues = new ArrayMap<>(1);
3183                     }
3184                     SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
3185                     if (valuesByParent == null) {
3186                         // We don't know the size yet, but usually it will be just a few fields...
3187                         valuesByParent = new SparseArray<>(5);
3188                         virtualValues.put(view, valuesByParent);
3189                     }
3190                     valuesByParent.put(id.getVirtualChildIntId(), value);
3191                 } else {
3192                     view.autofill(value);
3193 
3194                     // Set as autofilled if the values match now, e.g. when the value was updated
3195                     // synchronously.
3196                     // If autofill happens async, the view is set to autofilled in
3197                     // notifyValueChanged.
3198                     setAutofilledIfValuesIs(view, value, hideHighlight);
3199 
3200                     numApplied++;
3201                 }
3202             }
3203 
3204             handleFailedIdsLocked(failedIds);
3205 
3206             if (virtualValues != null) {
3207                 for (int i = 0; i < virtualValues.size(); i++) {
3208                     final View parent = virtualValues.keyAt(i);
3209                     final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
3210                     parent.autofill(childrenValues);
3211                     numApplied += childrenValues.size();
3212                     // TODO: we should provide a callback so the parent can call failures; something
3213                     // like notifyAutofillFailed(View view, int[] childrenIds);
3214                 }
3215             }
3216 
3217             mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
3218                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
3219                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
3220         }
3221     }
3222 
autofillContent(int sessionId, AutofillId id, ClipData clip)3223     private void autofillContent(int sessionId, AutofillId id, ClipData clip) {
3224         synchronized (mLock) {
3225             if (sessionId != mSessionId) {
3226                 return;
3227             }
3228             final AutofillClient client = getClient();
3229             if (client == null) {
3230                 return;
3231             }
3232             final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3233             if (view == null) {
3234                 // Most likely view has been removed after the initial request was sent to the
3235                 // the service; this is fine, but we need to update the view status in the
3236                 // server side so it can be triggered again.
3237                 Log.d(TAG, "autofillContent(): no view with id " + id);
3238                 reportAutofillContentFailure(id);
3239                 return;
3240             }
3241             ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
3242             ContentInfo result = view.performReceiveContent(payload);
3243             if (result != null) {
3244                 Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id
3245                         + ", view=" + view + ", clip=" + clip);
3246                 reportAutofillContentFailure(id);
3247                 return;
3248             }
3249             mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
3250                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, 1)
3251                     .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, 1));
3252         }
3253     }
3254 
reportAutofillContentFailure(AutofillId id)3255     private void reportAutofillContentFailure(AutofillId id) {
3256         try {
3257             mService.setAutofillFailure(mSessionId, Collections.singletonList(id),
3258                     mContext.getUserId());
3259         } catch (RemoteException e) {
3260             throw e.rethrowFromSystemServer();
3261         }
3262     }
3263 
newLog(int category)3264     private LogMaker newLog(int category) {
3265         final LogMaker log = new LogMaker(category)
3266                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
3267 
3268         if (isCompatibilityModeEnabledLocked()) {
3269             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
3270         }
3271         final AutofillClient client = getClient();
3272         if (client == null) {
3273             // Client should never be null here, but it doesn't hurt to check...
3274             log.setPackageName(mContext.getPackageName());
3275         } else {
3276             // Remove activity name from logging
3277             final ComponentName sanitizedComponentName =
3278                     new ComponentName(client.autofillClientGetComponentName().getPackageName(), "");
3279             log.setComponentName(sanitizedComponentName);
3280         }
3281         return log;
3282     }
3283 
3284     /**
3285      *  Set the tracked views.
3286      *
3287      * @param trackedIds The views to be tracked.
3288      * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
3289      * @param saveOnFinish Finish the session once the activity is finished.
3290      * @param fillableIds Views that might anchor FillUI.
3291      * @param saveTriggerId View that when clicked triggers commit().
3292      */
setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, boolean saveOnAllViewsInvisible, boolean saveOnFinish, @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId)3293     private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
3294             boolean saveOnAllViewsInvisible, boolean saveOnFinish,
3295             @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
3296         if (saveTriggerId != null) {
3297             saveTriggerId.resetSessionId();
3298         }
3299         synchronized (mLock) {
3300             if (sVerbose) {
3301                 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
3302                         + ", trackedIds=" + Arrays.toString(trackedIds)
3303                         + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible
3304                         + ", saveOnFinish=" + saveOnFinish
3305                         + ", fillableIds=" + Arrays.toString(fillableIds)
3306                         + ", saveTrigerId=" + saveTriggerId
3307                         + ", mFillableIds=" + mFillableIds
3308                         + ", mEnabled=" + mEnabled
3309                         + ", mSessionId=" + mSessionId);
3310             }
3311 
3312             if (mEnabled && mSessionId == sessionId) {
3313                 mSaveOnFinish = saveOnFinish;
3314                 if (fillableIds != null) {
3315                     if (mFillableIds == null) {
3316                         mFillableIds = new ArraySet<>(fillableIds.length);
3317                     }
3318                     for (AutofillId id : fillableIds) {
3319                         if (id != null) {
3320                             id.resetSessionId();
3321                             mFillableIds.add(id);
3322                         }
3323                     }
3324                 }
3325 
3326                 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
3327                     // Turn off trigger on previous view id.
3328                     setNotifyOnClickLocked(mSaveTriggerId, false);
3329                 }
3330 
3331                 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
3332                     // Turn on trigger on new view id.
3333                     mSaveTriggerId = saveTriggerId;
3334                     setNotifyOnClickLocked(mSaveTriggerId, true);
3335                 }
3336 
3337                 if (!saveOnAllViewsInvisible) {
3338                     trackedIds = null;
3339                 }
3340 
3341                 final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
3342                 if (mFillableIds != null) {
3343                     allFillableIds.addAll(mFillableIds);
3344                 }
3345                 if (trackedIds != null) {
3346                     for (AutofillId id : trackedIds) {
3347                         if (id != null) {
3348                             id.resetSessionId();
3349                             allFillableIds.add(id);
3350                         }
3351                     }
3352                 }
3353 
3354                 if (!allFillableIds.isEmpty()) {
3355                     mTrackedViews = new TrackedViews(trackedIds, Helper.toArray(allFillableIds));
3356                 } else {
3357                     mTrackedViews = null;
3358                 }
3359             }
3360         }
3361     }
3362 
setNotifyOnClickLocked(@onNull AutofillId id, boolean notify)3363     private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
3364         final View view = findView(id);
3365         if (view == null) {
3366             Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
3367             return;
3368         }
3369         view.setNotifyAutofillManagerOnClick(notify);
3370     }
3371 
setSaveUiState(int sessionId, boolean shown)3372     private void setSaveUiState(int sessionId, boolean shown) {
3373         if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
3374         synchronized (mLock) {
3375             if (mSessionId != NO_SESSION) {
3376                 // Race condition: app triggered a new session after the previous session was
3377                 // finished but before server called setSaveUiState() - need to cancel the new
3378                 // session to avoid further inconsistent behavior.
3379                 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
3380                         + ") called on existing session " + mSessionId + "; cancelling it");
3381                 cancelSessionLocked();
3382             }
3383             if (shown) {
3384                 mSessionId = sessionId;
3385                 mState = STATE_SHOWING_SAVE_UI;
3386             } else {
3387                 mSessionId = NO_SESSION;
3388                 mState = STATE_UNKNOWN;
3389             }
3390         }
3391     }
3392 
3393     /**
3394      * Marks the state of the session as finished.
3395      *
3396      * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
3397      *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
3398      *  {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
3399      *  changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
3400      *  when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
3401      *  (because the autofill service disabled further autofill requests for the activity).
3402      * @param autofillableIds list of ids that could trigger autofill, use to not handle a new
3403      *  session when they're entered.
3404      */
setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds)3405     private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
3406         if (autofillableIds != null) {
3407             for (int i = 0; i < autofillableIds.size(); i++) {
3408                 autofillableIds.get(i).resetSessionId();
3409             }
3410         }
3411         synchronized (mLock) {
3412             if (sVerbose) {
3413                 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
3414                         + getStateAsString(newState) + "; autofillableIds=" + autofillableIds);
3415             }
3416             if (autofillableIds != null) {
3417                 mEnteredIds = new ArraySet<>(autofillableIds);
3418             }
3419             if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
3420                 resetSessionLocked(/* resetEnteredIds= */ true);
3421                 mState = STATE_UNKNOWN;
3422             } else {
3423                 resetSessionLocked(/* resetEnteredIds= */ false);
3424                 mState = newState;
3425             }
3426         }
3427     }
3428 
3429     /**
3430      * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
3431      *
3432      * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
3433      * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
3434      */
getAugmentedAutofillClient(@onNull IResultReceiver result)3435     private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
3436         synchronized (mLock) {
3437             if (mAugmentedAutofillServiceClient == null) {
3438                 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
3439             }
3440             final Bundle resultData = new Bundle();
3441             resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
3442                     mAugmentedAutofillServiceClient.asBinder());
3443 
3444             try {
3445                 result.send(0, resultData);
3446             } catch (RemoteException e) {
3447                 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
3448             }
3449         }
3450     }
3451 
requestShowSoftInput(@onNull AutofillId id)3452     private void requestShowSoftInput(@NonNull AutofillId id) {
3453         if (sVerbose) Log.v(TAG, "requestShowSoftInput(" + id + ")");
3454         final AutofillClient client = getClient();
3455         if (client == null) {
3456             return;
3457         }
3458         final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3459         if (view == null) {
3460             if (sVerbose) Log.v(TAG, "View is not found");
3461             return;
3462         }
3463         final Handler handler = view.getHandler();
3464         if (handler == null) {
3465             if (sVerbose) Log.v(TAG, "Ignoring requestShowSoftInput due to no handler in view");
3466             return;
3467         }
3468         if (handler.getLooper() != Looper.myLooper()) {
3469             // The view is running on a different thread than our own, so we need to reschedule
3470             // our work for over there.
3471             if (sVerbose) Log.v(TAG, "Scheduling showSoftInput() on the view UI thread");
3472             handler.post(() -> requestShowSoftInputInViewThread(view));
3473         } else {
3474             requestShowSoftInputInViewThread(view);
3475         }
3476     }
3477 
3478     // This method must be called from within the View thread.
requestShowSoftInputInViewThread(@onNull View view)3479     private static void requestShowSoftInputInViewThread(@NonNull View view) {
3480         if (!view.isFocused()) {
3481             Log.w(TAG, "Ignoring requestShowSoftInput() due to non-focused view");
3482             return;
3483         }
3484         final InputMethodManager inputMethodManager = view.getContext().getSystemService(
3485                 InputMethodManager.class);
3486         boolean ret = inputMethodManager.showSoftInput(view, /*flags=*/ 0);
3487         if (sVerbose) Log.v(TAG, " InputMethodManager.showSoftInput returns " + ret);
3488     }
3489 
3490     /** @hide */
requestHideFillUi()3491     public void requestHideFillUi() {
3492         requestHideFillUi(mIdShownFillUi, true);
3493     }
3494 
requestHideFillUi(AutofillId id, boolean force)3495     private void requestHideFillUi(AutofillId id, boolean force) {
3496         final View anchor = id == null ? null : findView(id);
3497         if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
3498         if (anchor == null) {
3499             if (force) {
3500                 // When user taps outside autofill window, force to close fill ui even id does
3501                 // not match.
3502                 AutofillClient client = getClient();
3503                 if (client != null) {
3504                     client.autofillClientRequestHideFillUi();
3505                 }
3506             }
3507             return;
3508         }
3509         requestHideFillUi(id, anchor);
3510     }
3511 
requestHideFillUi(AutofillId id, View anchor)3512     private void requestHideFillUi(AutofillId id, View anchor) {
3513 
3514         AutofillCallback callback = null;
3515         synchronized (mLock) {
3516             // We do not check the session id for two reasons:
3517             // 1. If local and remote session id are off sync the UI would be stuck shown
3518             // 2. There is a race between the user state being destroyed due the fill
3519             //    service being uninstalled and the UI being dismissed.
3520             AutofillClient client = getClient();
3521             if (client != null) {
3522                 if (client.autofillClientRequestHideFillUi()) {
3523                     mIdShownFillUi = null;
3524                     callback = mCallback;
3525                 }
3526             }
3527         }
3528 
3529         if (callback != null) {
3530             if (id.isVirtualInt()) {
3531                 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
3532                         AutofillCallback.EVENT_INPUT_HIDDEN);
3533             } else {
3534                 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
3535             }
3536         }
3537     }
3538 
notifyDisableAutofill(long disableDuration, ComponentName componentName)3539     private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
3540         synchronized (mLock) {
3541             if (mOptions == null) {
3542                 return;
3543             }
3544             long expiration = SystemClock.elapsedRealtime() + disableDuration;
3545             // Protect it against overflow
3546             if (expiration < 0) {
3547                 expiration = Long.MAX_VALUE;
3548             }
3549             if (componentName != null) {
3550                 if (mOptions.disabledActivities == null) {
3551                     mOptions.disabledActivities = new ArrayMap<>();
3552                 }
3553                 mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
3554             } else {
3555                 mOptions.appDisabledExpiration = expiration;
3556             }
3557         }
3558     }
3559 
notifyReenableAutofill()3560     void notifyReenableAutofill() {
3561         synchronized (mLock) {
3562             if (mOptions == null) {
3563                 return;
3564             }
3565             mOptions.appDisabledExpiration = 0;
3566             mOptions.disabledActivities = null;
3567         }
3568     }
3569 
notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState)3570     private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
3571         if (sVerbose) {
3572             Log.v(TAG, "notifyNoFillUi(): sessionFinishedState=" + sessionFinishedState);
3573         }
3574         final View anchor = findView(id);
3575         if (anchor == null) {
3576             return;
3577         }
3578 
3579         notifyCallback(sessionId, id, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
3580 
3581         if (sessionFinishedState != STATE_UNKNOWN) {
3582             // Callback call was "hijacked" to also update the session state.
3583             setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
3584         }
3585     }
3586 
notifyCallback( int sessionId, AutofillId id, @AutofillCallback.AutofillEventType int event)3587     private void notifyCallback(
3588             int sessionId, AutofillId id, @AutofillCallback.AutofillEventType int event) {
3589         if (sVerbose) {
3590             Log.v(TAG, "notifyCallback(): sessionId=" + sessionId + ", autofillId=" + id
3591                     + ", event=" + event);
3592         }
3593         final View anchor = findView(id);
3594         if (anchor == null) {
3595             return;
3596         }
3597 
3598         AutofillCallback callback = null;
3599         synchronized (mLock) {
3600             if (mSessionId == sessionId && getClient() != null) {
3601                 callback = mCallback;
3602             }
3603         }
3604 
3605         if (callback != null) {
3606             if (id.isVirtualInt()) {
3607                 callback.onAutofillEvent(
3608                         anchor, id.getVirtualChildIntId(), event);
3609             } else {
3610                 callback.onAutofillEvent(anchor, event);
3611             }
3612         }
3613     }
3614 
shouldSuppressDialogsForCredman(View view)3615     private boolean shouldSuppressDialogsForCredman(View view) {
3616         if (view == null) {
3617             return false;
3618         }
3619         // isCredential field indicates that the developer might be calling Credman, and we should
3620         // suppress autofill dialogs. But it is not a good enough indicator that there is a valid
3621         // credman option.
3622         return view.isCredential() || isCredmanRequested(view);
3623     }
3624 
isCredmanRequested(View view)3625     private boolean isCredmanRequested(View view) {
3626         if (view == null) {
3627             return false;
3628         }
3629         return view.getViewCredentialHandler() != null;
3630     }
3631 
3632     /**
3633      * Find a single view by its id.
3634      *
3635      * @param autofillId The autofill id of the view
3636      *
3637      * @return The view or {@code null} if view was not found
3638      */
findView(@onNull AutofillId autofillId)3639     private View findView(@NonNull AutofillId autofillId) {
3640         final AutofillClient client = getClient();
3641         if (client != null) {
3642             return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
3643         }
3644         return null;
3645     }
3646 
3647     /** @hide */
hasAutofillFeature()3648     public boolean hasAutofillFeature() {
3649         return mService != null;
3650     }
3651 
3652     /** @hide */
onPendingSaveUi(int operation, IBinder token)3653     public void onPendingSaveUi(int operation, IBinder token) {
3654         if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
3655 
3656         synchronized (mLock) {
3657             try {
3658                 mService.onPendingSaveUi(operation, token);
3659             } catch (RemoteException e) {
3660                 Log.e(TAG, "Error in onPendingSaveUi: ", e);
3661             }
3662         }
3663     }
3664 
3665     /** @hide */
dump(String outerPrefix, PrintWriter pw)3666     public void dump(String outerPrefix, PrintWriter pw) {
3667         synchronized (mLock) {
3668             pw.print(outerPrefix); pw.println("AutofillManager:");
3669             final String pfx = outerPrefix + "  ";
3670             pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
3671             pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
3672             pw.print(pfx); pw.print("context: "); pw.println(mContext);
3673             pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
3674             final AutofillClient client = getClient();
3675             if (client != null) {
3676                 pw.print(pfx); pw.print("client: "); pw.print(client);
3677                 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
3678             }
3679             pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
3680             pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
3681             pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
3682             pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
3683             pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
3684             pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
3685             pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
3686             pw.print(pfx); pw.print("tracked views: ");
3687             if (mTrackedViews == null) {
3688                 pw.println("null");
3689             } else {
3690                 final String pfx2 = pfx + "  ";
3691                 pw.println();
3692                 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
3693                 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
3694             }
3695             pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
3696             pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
3697             if (mEnteredForAugmentedAutofillIds != null) {
3698                 pw.print(pfx); pw.print("entered ids for augmented autofill: ");
3699                 pw.println(mEnteredForAugmentedAutofillIds);
3700             }
3701             if (mForAugmentedAutofillOnly) {
3702                 pw.print(pfx); pw.println("For Augmented Autofill Only");
3703             }
3704             pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
3705             pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
3706             if (mOptions != null) {
3707                 pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
3708             }
3709             pw.print(pfx); pw.print("fill dialog enabled: "); pw.println(mIsFillDialogEnabled);
3710             pw.print(pfx); pw.print("fill dialog enabled hints: ");
3711             pw.println(Arrays.toString(mFillDialogEnabledHints));
3712             pw.print(pfx); pw.print("compat mode enabled: ");
3713             if (mCompatibilityBridge != null) {
3714                 final String pfx2 = pfx + "  ";
3715                 pw.println("true");
3716                 pw.print(pfx2); pw.print("windowId: ");
3717                 pw.println(mCompatibilityBridge.mFocusedWindowId);
3718                 pw.print(pfx2); pw.print("nodeId: ");
3719                 pw.println(mCompatibilityBridge.mFocusedNodeId);
3720                 pw.print(pfx2); pw.print("virtualId: ");
3721                 pw.println(AccessibilityNodeInfo
3722                         .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
3723                 pw.print(pfx2); pw.print("focusedBounds: ");
3724                 pw.println(mCompatibilityBridge.mFocusedBounds);
3725             } else {
3726                 pw.println("false");
3727             }
3728             pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
3729             pw.print(" verbose: "); pw.println(sVerbose);
3730         }
3731     }
3732 
3733     @GuardedBy("mLock")
getStateAsStringLocked()3734     private String getStateAsStringLocked() {
3735         return getStateAsString(mState);
3736     }
3737 
3738     @NonNull
getStateAsString(int state)3739     private static String getStateAsString(int state) {
3740         switch (state) {
3741             case STATE_UNKNOWN:
3742                 return "UNKNOWN";
3743             case STATE_ACTIVE:
3744                 return "ACTIVE";
3745             case STATE_PENDING_AUTHENTICATION:
3746                 return "PENDING_AUTHENTICATION";
3747             case STATE_FINISHED:
3748                 return "FINISHED";
3749             case STATE_SHOWING_SAVE_UI:
3750                 return "SHOWING_SAVE_UI";
3751             case STATE_DISABLED_BY_SERVICE:
3752                 return "DISABLED_BY_SERVICE";
3753             case STATE_UNKNOWN_COMPAT_MODE:
3754                 return "UNKNOWN_COMPAT_MODE";
3755             case STATE_UNKNOWN_FAILED:
3756                 return "UNKNOWN_FAILED";
3757             default:
3758                 return "INVALID:" + state;
3759         }
3760     }
3761 
3762     /** @hide */
getSmartSuggestionModeToString(@martSuggestionMode int flags)3763     public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
3764         switch (flags) {
3765             case FLAG_SMART_SUGGESTION_OFF:
3766                 return "OFF";
3767             case FLAG_SMART_SUGGESTION_SYSTEM:
3768                 return "SYSTEM";
3769             default:
3770                 return "INVALID:" + flags;
3771         }
3772     }
3773 
3774     @GuardedBy("mLock")
isActiveLocked()3775     private boolean isActiveLocked() {
3776         return mState == STATE_ACTIVE || isPendingAuthenticationLocked();
3777     }
3778 
3779     @GuardedBy("mLock")
isPendingAuthenticationLocked()3780     private boolean isPendingAuthenticationLocked() {
3781         return mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION;
3782     }
3783 
3784     @GuardedBy("mLock")
isDisabledByServiceLocked()3785     private boolean isDisabledByServiceLocked() {
3786         return mState == STATE_DISABLED_BY_SERVICE;
3787     }
3788 
3789     @GuardedBy("mLock")
isFinishedLocked()3790     private boolean isFinishedLocked() {
3791         return mState == STATE_FINISHED;
3792     }
3793 
post(Runnable runnable)3794     private void post(Runnable runnable) {
3795         final AutofillClient client = getClient();
3796         if (client == null) {
3797             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
3798             return;
3799         }
3800         client.autofillClientRunOnUiThread(runnable);
3801     }
3802 
setFillDialogTriggerIds(@ullable List<AutofillId> ids)3803     private void setFillDialogTriggerIds(@Nullable List<AutofillId> ids) {
3804         mFillDialogTriggerIds = ids;
3805     }
3806 
3807     /**
3808      * If autofill suggestions for a
3809      * <a href="{@docRoot}reference/android/service/autofill/Dataset.html#FillDialogUI">
3810      * dialog-style UI</a> are available for {@code view}, shows a dialog allowing the user to
3811      * select a suggestion and returns {@code true}.
3812      * <p>
3813      * The dialog may not be shown if the autofill service does not support it, if the autofill
3814      * request has not returned a response yet, if the dialog was shown previously, or if the
3815      * input method is already shown.
3816      * <p>
3817      * It is recommended apps to call this method the first time a user focuses on
3818      * an autofill-able form, and to avoid showing the input method if the dialog is shown. If
3819      * this method returns {@code false}, you should then instead show the input method (assuming
3820      * that is how the view normally handles the focus event). If the user re-focuses on the view,
3821      * you should not call this method again so as to not disrupt usage of the input method.
3822      *
3823      * @param view the view for which to show autofill suggestions. This is typically a view
3824      *             receiving a focus event. The autofill suggestions shown will include content for
3825      *             related views as well.
3826      * @return {@code true} if the autofill dialog is being shown
3827      */
3828     // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
showAutofillDialog(@onNull View view)3829     public boolean showAutofillDialog(@NonNull View view) {
3830         Objects.requireNonNull(view);
3831         if (shouldShowAutofillDialog(view, view.getAutofillId())) {
3832             mShowAutofillDialogCalled = true;
3833             final WeakReference<View> wrView = new WeakReference<>(view);
3834             // The id matches a trigger id, this will trigger the fill dialog.
3835             post(() -> {
3836                 final View v = wrView.get();
3837                 if (v != null) {
3838                     notifyViewEntered(v);
3839                 }
3840             });
3841             return true;
3842         }
3843         return false;
3844     }
3845 
3846     /**
3847      * If autofill suggestions for a
3848      * <a href="{@docRoot}reference/android/service/autofill/Dataset.html#FillDialogUI">
3849      * dialog-style UI</a> are available for virtual {@code view}, shows a dialog allowing the user
3850      * to select a suggestion and returns {@code true}.
3851      * <p>
3852      * The dialog may not be shown if the autofill service does not support it, if the autofill
3853      * request has not returned a response yet, if the dialog was shown previously, or if the
3854      * input method is already shown.
3855      * <p>
3856      * It is recommended apps to call this method the first time a user focuses on
3857      * an autofill-able form, and to avoid showing the input method if the dialog is shown. If
3858      * this method returns {@code false}, you should then instead show the input method (assuming
3859      * that is how the view normally handles the focus event). If the user re-focuses on the view,
3860      * you should not call this method again so as to not disrupt usage of the input method.
3861      *
3862      * @param view the view hosting the virtual view hierarchy which is used to show autofill
3863      *            suggestions.
3864      * @param virtualId id identifying the virtual view inside the host view.
3865      * @return {@code true} if the autofill dialog is being shown
3866      */
showAutofillDialog(@onNull View view, int virtualId)3867     public boolean showAutofillDialog(@NonNull View view, int virtualId) {
3868         Objects.requireNonNull(view);
3869         if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
3870             mShowAutofillDialogCalled = true;
3871             final WeakReference<View> wrView = new WeakReference<>(view);
3872             // The id matches a trigger id, this will trigger the fill dialog.
3873             post(() -> {
3874                 final View v = wrView.get();
3875                 if (v != null) {
3876                     notifyViewEntered(v, virtualId, /* bounds= */ null, /* flags= */ 0);
3877                 }
3878             });
3879             return true;
3880         }
3881         return false;
3882     }
3883 
shouldShowAutofillDialog(View view, AutofillId id)3884     private boolean shouldShowAutofillDialog(View view, AutofillId id) {
3885         if (!hasFillDialogUiFeature()
3886                 || mShowAutofillDialogCalled
3887                 || mFillDialogTriggerIds == null
3888                 || mScreenHasCredmanField) {
3889             return false;
3890         }
3891 
3892         if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
3893             // IME is showing
3894             return false;
3895         }
3896 
3897         final int size = mFillDialogTriggerIds.size();
3898         for (int i = 0; i < size; i++) {
3899             AutofillId fillId = mFillDialogTriggerIds.get(i);
3900             if (fillId.equalsIgnoreSession(id)) {
3901                 return true;
3902             }
3903         }
3904         return false;
3905     }
3906 
3907     /**
3908      * Implementation of the accessibility based compatibility.
3909      */
3910     private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
3911         @GuardedBy("mLock")
3912         private final Rect mFocusedBounds = new Rect();
3913         @GuardedBy("mLock")
3914         private final Rect mTempBounds = new Rect();
3915 
3916         @GuardedBy("mLock")
3917         private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
3918         @GuardedBy("mLock")
3919         private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
3920 
3921         // Need to report a fake service in case a11y clients check the service list
3922         @NonNull
3923         @GuardedBy("mLock")
3924         AccessibilityServiceInfo mCompatServiceInfo;
3925 
CompatibilityBridge()3926         CompatibilityBridge() {
3927             final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
3928             am.setAccessibilityPolicy(this);
3929         }
3930 
getCompatServiceInfo()3931         private AccessibilityServiceInfo getCompatServiceInfo() {
3932             synchronized (mLock) {
3933                 if (mCompatServiceInfo != null) {
3934                     return mCompatServiceInfo;
3935                 }
3936                 final Intent intent = new Intent();
3937                 intent.setComponent(new ComponentName("android",
3938                         "com.android.server.autofill.AutofillCompatAccessibilityService"));
3939                 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
3940                         intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
3941                 try {
3942                     mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
3943                 } catch (XmlPullParserException | IOException e) {
3944                     Log.e(TAG, "Cannot find compat autofill service:" + intent);
3945                     throw new IllegalStateException("Cannot find compat autofill service");
3946                 }
3947                 return mCompatServiceInfo;
3948             }
3949         }
3950 
3951         @Override
isEnabled(boolean accessibilityEnabled)3952         public boolean isEnabled(boolean accessibilityEnabled) {
3953             return true;
3954         }
3955 
3956         @Override
getRelevantEventTypes(int relevantEventTypes)3957         public int getRelevantEventTypes(int relevantEventTypes) {
3958             return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
3959                     | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
3960                     | AccessibilityEvent.TYPE_VIEW_CLICKED
3961                     | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
3962         }
3963 
3964         @Override
getInstalledAccessibilityServiceList( List<AccessibilityServiceInfo> installedServices)3965         public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
3966                List<AccessibilityServiceInfo> installedServices) {
3967             if (installedServices == null) {
3968                 installedServices = new ArrayList<>();
3969             }
3970             installedServices.add(getCompatServiceInfo());
3971             return installedServices;
3972         }
3973 
3974         @Override
getEnabledAccessibilityServiceList( int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService)3975         public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
3976                 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
3977             if (enabledService == null) {
3978                 enabledService = new ArrayList<>();
3979             }
3980             enabledService.add(getCompatServiceInfo());
3981             return enabledService;
3982         }
3983 
3984         @Override
onAccessibilityEvent(AccessibilityEvent event, boolean accessibilityEnabled, int relevantEventTypes)3985         public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
3986                 boolean accessibilityEnabled, int relevantEventTypes) {
3987             final int type = event.getEventType();
3988             if (sVerbose) {
3989                 // NOTE: this is waaay spammy, but that's life.
3990                 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
3991                         + "): virtualId="
3992                         + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
3993                         + ", client=" + getClient());
3994             }
3995             switch (type) {
3996                 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
3997                     synchronized (mLock) {
3998                         if (mFocusedWindowId == event.getWindowId()
3999                                 && mFocusedNodeId == event.getSourceNodeId()) {
4000                             return event;
4001                         }
4002                         if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
4003                                 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
4004                             notifyViewExited(mFocusedWindowId, mFocusedNodeId);
4005                             mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
4006                             mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
4007                             mFocusedBounds.set(0, 0, 0, 0);
4008                         }
4009                         final int windowId = event.getWindowId();
4010                         final long nodeId = event.getSourceNodeId();
4011                         if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
4012                             mFocusedWindowId = windowId;
4013                             mFocusedNodeId = nodeId;
4014                         }
4015                     }
4016                 } break;
4017 
4018                 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
4019                     synchronized (mLock) {
4020                         if (mFocusedWindowId == event.getWindowId()
4021                                 && mFocusedNodeId == event.getSourceNodeId()) {
4022                             notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
4023                         }
4024                     }
4025                 } break;
4026 
4027                 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
4028                     synchronized (mLock) {
4029                         notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
4030                     }
4031                 } break;
4032 
4033                 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
4034                     final AutofillClient client = getClient();
4035                     if (client != null) {
4036                         synchronized (mLock) {
4037                             if (client.autofillClientIsFillUiShowing()) {
4038                                 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
4039                             }
4040                             updateTrackedViewsLocked();
4041                         }
4042                     }
4043                 } break;
4044             }
4045 
4046             return accessibilityEnabled ? event : null;
4047         }
4048 
notifyViewEntered(int windowId, long nodeId, Rect focusedBounds)4049         private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
4050             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
4051             if (!isVirtualNode(virtualId)) {
4052                 return false;
4053             }
4054             final View view = findViewByAccessibilityId(windowId, nodeId);
4055             if (view == null) {
4056                 return false;
4057             }
4058             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
4059             if (node == null) {
4060                 return false;
4061             }
4062             if (!node.isEditable()) {
4063                 return false;
4064             }
4065             final Rect newBounds = mTempBounds;
4066             node.getBoundsInScreen(newBounds);
4067             if (newBounds.equals(focusedBounds)) {
4068                 return false;
4069             }
4070             focusedBounds.set(newBounds);
4071             AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
4072             return true;
4073         }
4074 
notifyViewExited(int windowId, long nodeId)4075         private void notifyViewExited(int windowId, long nodeId) {
4076             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
4077             if (!isVirtualNode(virtualId)) {
4078                 return;
4079             }
4080             final View view = findViewByAccessibilityId(windowId, nodeId);
4081             if (view == null) {
4082                 return;
4083             }
4084             AutofillManager.this.notifyViewExited(view, virtualId);
4085         }
4086 
notifyValueChanged(int windowId, long nodeId)4087         private void notifyValueChanged(int windowId, long nodeId) {
4088             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
4089             if (!isVirtualNode(virtualId)) {
4090                 return;
4091             }
4092             final View view = findViewByAccessibilityId(windowId, nodeId);
4093             if (view == null) {
4094                 return;
4095             }
4096             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
4097             if (node == null) {
4098                 return;
4099             }
4100             AutofillManager.this.notifyValueChanged(view, virtualId,
4101                     AutofillValue.forText(node.getText()));
4102         }
4103 
notifyViewClicked(int windowId, long nodeId)4104         private void notifyViewClicked(int windowId, long nodeId) {
4105             final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
4106             if (!isVirtualNode(virtualId)) {
4107                 return;
4108             }
4109             final View view = findViewByAccessibilityId(windowId, nodeId);
4110             if (view == null) {
4111                 return;
4112             }
4113             final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
4114             if (node == null) {
4115                 return;
4116             }
4117             AutofillManager.this.notifyViewClicked(view, virtualId);
4118         }
4119 
4120         @GuardedBy("mLock")
updateTrackedViewsLocked()4121         private void updateTrackedViewsLocked() {
4122             if (mTrackedViews != null) {
4123                 mTrackedViews.onVisibleForAutofillChangedLocked();
4124             }
4125         }
4126 
findViewByAccessibilityId(int windowId, long nodeId)4127         private View findViewByAccessibilityId(int windowId, long nodeId) {
4128             final AutofillClient client = getClient();
4129             if (client == null) {
4130                 return null;
4131             }
4132             final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
4133             return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
4134         }
4135 
findVirtualNodeByAccessibilityId(View view, int virtualId)4136         private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
4137             final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
4138             if (provider == null) {
4139                 return null;
4140             }
4141             return provider.createAccessibilityNodeInfo(virtualId);
4142         }
4143 
isVirtualNode(int nodeId)4144         private boolean isVirtualNode(int nodeId) {
4145             return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
4146                     && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
4147         }
4148     }
4149 
4150     /**
4151      * View tracking information. Once all tracked views become invisible the session is finished.
4152      */
4153     private class TrackedViews {
4154         /** Visible tracked views */
4155         @NonNull private final ArraySet<AutofillId> mVisibleTrackedIds;
4156 
4157         /** Invisible tracked views */
4158         @NonNull private final ArraySet<AutofillId> mInvisibleTrackedIds;
4159 
4160         /** Visible tracked views for fill dialog */
4161         @NonNull private final ArraySet<AutofillId> mVisibleDialogTrackedIds;
4162 
4163         /** Invisible tracked views for fill dialog */
4164         @NonNull private final ArraySet<AutofillId> mInvisibleDialogTrackedIds;
4165 
4166         boolean mHasNewTrackedView;
4167         boolean mIsTrackedSaveView;
4168 
4169         /**
4170          * Check if set is null or value is in set.
4171          *
4172          * @param set   The set or null (== empty set)
4173          * @param value The value that might be in the set
4174          *
4175          * @return {@code true} iff set is not empty and value is in set
4176          */
4177         // TODO: move to Helper as static method
isInSet(@ullable ArraySet<T> set, T value)4178         private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
4179             return set != null && set.contains(value);
4180         }
4181 
4182         /**
4183          * Add a value to a set. If set is null, create a new set.
4184          *
4185          * @param set        The set or null (== empty set)
4186          * @param valueToAdd The value to add
4187          *
4188          * @return The set including the new value. If set was {@code null}, a set containing only
4189          *         the new value.
4190          */
4191         // TODO: move to Helper as static method
4192         @NonNull
addToSet(@ullable ArraySet<T> set, T valueToAdd)4193         private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
4194             if (set == null) {
4195                 set = new ArraySet<>(1);
4196             }
4197 
4198             set.add(valueToAdd);
4199 
4200             return set;
4201         }
4202 
4203         /**
4204          * Remove a value from a set.
4205          *
4206          * @param set           The set or null (== empty set)
4207          * @param valueToRemove The value to remove
4208          *
4209          * @return The set without the removed value. {@code null} if set was null, or is empty
4210          *         after removal.
4211          */
4212         // TODO: move to Helper as static method
4213         @Nullable
removeFromSet(@ullable ArraySet<T> set, T valueToRemove)4214         private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
4215             if (set == null) {
4216                 return null;
4217             }
4218 
4219             set.remove(valueToRemove);
4220 
4221             if (set.isEmpty()) {
4222                 return null;
4223             }
4224 
4225             return set;
4226         }
4227 
4228         /**
4229          * Set the tracked views.
4230          *
4231          * @param trackedIds The views to be tracked
4232          */
TrackedViews(@ullable AutofillId[] trackedIds, @Nullable AutofillId[] allTrackedIds)4233         TrackedViews(@Nullable AutofillId[] trackedIds, @Nullable AutofillId[] allTrackedIds) {
4234             mVisibleTrackedIds = new ArraySet<>();
4235             mInvisibleTrackedIds = new ArraySet<>();
4236             if (!ArrayUtils.isEmpty(trackedIds)) {
4237                 mIsTrackedSaveView = true;
4238                 initialTrackedViews(trackedIds, mVisibleTrackedIds, mInvisibleTrackedIds);
4239             }
4240 
4241             mVisibleDialogTrackedIds = new ArraySet<>();
4242             mInvisibleDialogTrackedIds = new ArraySet<>();
4243             if (!ArrayUtils.isEmpty(allTrackedIds)) {
4244                 initialTrackedViews(allTrackedIds, mVisibleDialogTrackedIds,
4245                         mInvisibleDialogTrackedIds);
4246                 mAllTrackedViews.addAll(Arrays.asList(allTrackedIds));
4247             }
4248 
4249             if (sVerbose) {
4250                 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
4251                         + " mVisibleTrackedIds=" + mVisibleTrackedIds
4252                         + " mInvisibleTrackedIds=" + mInvisibleTrackedIds
4253                         + " allTrackedIds=" + Arrays.toString(allTrackedIds)
4254                         + " mVisibleDialogTrackedIds=" + mVisibleDialogTrackedIds
4255                         + " mInvisibleDialogTrackedIds=" + mInvisibleDialogTrackedIds);
4256             }
4257 
4258             if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
4259                 finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
4260             }
4261         }
4262 
initialTrackedViews(AutofillId[] trackedIds, @NonNull ArraySet<AutofillId> visibleSet, @NonNull ArraySet<AutofillId> invisibleSet)4263         private void initialTrackedViews(AutofillId[] trackedIds,
4264                 @NonNull ArraySet<AutofillId> visibleSet,
4265                 @NonNull ArraySet<AutofillId> invisibleSet) {
4266             final boolean[] isVisible;
4267             final AutofillClient client = getClient();
4268             if (ArrayUtils.isEmpty(trackedIds) || client == null) {
4269                 return;
4270             }
4271             if (client.autofillClientIsVisibleForAutofill()) {
4272                 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
4273                 isVisible = client.autofillClientGetViewVisibility(trackedIds);
4274             } else {
4275                 // All false
4276                 isVisible = new boolean[trackedIds.length];
4277             }
4278 
4279             final int numIds = trackedIds.length;
4280             for (int i = 0; i < numIds; i++) {
4281                 final AutofillId id = trackedIds[i];
4282                 id.resetSessionId();
4283 
4284                 if (isVisible[i]) {
4285                     addToSet(visibleSet, id);
4286                 } else {
4287                     addToSet(invisibleSet, id);
4288                 }
4289             }
4290         }
4291 
4292         /**
4293          * Called when a {@link View view's} visibility changes.
4294          *
4295          * @param id the id of the view/virtual view whose visibility changed.
4296          * @param isVisible visible if the view is visible in the view hierarchy.
4297          */
4298         @GuardedBy("mLock")
notifyViewVisibilityChangedLocked(@onNull AutofillId id, boolean isVisible)4299         void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
4300             if (sDebug) {
4301                 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
4302                         + isVisible);
4303             }
4304 
4305             if (isClientVisibleForAutofillLocked()) {
4306                 if (isVisible) {
4307                     if (isInSet(mInvisibleTrackedIds, id)) {
4308                         removeFromSet(mInvisibleTrackedIds, id);
4309                         addToSet(mVisibleTrackedIds, id);
4310                     }
4311                     if (isInSet(mInvisibleDialogTrackedIds, id)) {
4312                         removeFromSet(mInvisibleDialogTrackedIds, id);
4313                         addToSet(mVisibleDialogTrackedIds, id);
4314                     }
4315                 } else {
4316                     if (isInSet(mVisibleTrackedIds, id)) {
4317                         removeFromSet(mVisibleTrackedIds, id);
4318                         addToSet(mInvisibleTrackedIds, id);
4319                     }
4320                     if (isInSet(mVisibleDialogTrackedIds, id)) {
4321                         removeFromSet(mVisibleDialogTrackedIds, id);
4322                         addToSet(mInvisibleDialogTrackedIds, id);
4323                     }
4324                 }
4325             }
4326 
4327             if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
4328                 if (sVerbose) {
4329                     Log.v(TAG, "No more visible tracked save ids. Invisible = "
4330                             + mInvisibleTrackedIds);
4331                 }
4332                 finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
4333 
4334             }
4335             if (mVisibleDialogTrackedIds.isEmpty()) {
4336                 if (sVerbose) {
4337                     Log.v(TAG, "No more visible tracked fill dialog ids. Invisible = "
4338                             + mInvisibleDialogTrackedIds);
4339                 }
4340                 processNoVisibleTrackedAllViews();
4341             }
4342         }
4343 
4344         /**
4345          * Called once the client becomes visible.
4346          *
4347          * @see AutofillClient#autofillClientIsVisibleForAutofill()
4348          */
4349         @GuardedBy("mLock")
onVisibleForAutofillChangedLocked()4350         void onVisibleForAutofillChangedLocked() {
4351             // The visibility of the views might have changed while the client was not be visible,
4352             // hence update the visibility state for all views.
4353             AutofillClient client = getClient();
4354             if (client != null) {
4355                 if (sVerbose) {
4356                     Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
4357                             + " vis=" + mVisibleTrackedIds);
4358                 }
4359 
4360                 onVisibleForAutofillChangedInternalLocked(mVisibleTrackedIds, mInvisibleTrackedIds);
4361                 onVisibleForAutofillChangedInternalLocked(
4362                         mVisibleDialogTrackedIds, mInvisibleDialogTrackedIds);
4363             }
4364 
4365             if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
4366                 if (sVerbose) {
4367                     Log.v(TAG,  "onVisibleForAutofillChangedLocked(): no more visible ids");
4368                 }
4369                 finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
4370             }
4371             if (mVisibleDialogTrackedIds.isEmpty()) {
4372                 if (sVerbose) {
4373                     Log.v(TAG,  "onVisibleForAutofillChangedLocked(): no more visible ids");
4374                 }
4375                 processNoVisibleTrackedAllViews();
4376             }
4377         }
4378 
onVisibleForAutofillChangedInternalLocked(@onNull ArraySet<AutofillId> visibleSet, @NonNull ArraySet<AutofillId> invisibleSet)4379         void onVisibleForAutofillChangedInternalLocked(@NonNull ArraySet<AutofillId> visibleSet,
4380                 @NonNull ArraySet<AutofillId> invisibleSet) {
4381             // The visibility of the views might have changed while the client was not be visible,
4382             // hence update the visibility state for all views.
4383             if (sVerbose) {
4384                 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + invisibleSet
4385                         + " vis=" + visibleSet);
4386             }
4387 
4388             ArraySet<AutofillId> allTrackedIds = new ArraySet<>();
4389             allTrackedIds.addAll(visibleSet);
4390             allTrackedIds.addAll(invisibleSet);
4391             if (!allTrackedIds.isEmpty()) {
4392                 visibleSet.clear();
4393                 invisibleSet.clear();
4394                 initialTrackedViews(Helper.toArray(allTrackedIds), visibleSet, invisibleSet);
4395             }
4396         }
4397 
processNoVisibleTrackedAllViews()4398         private void processNoVisibleTrackedAllViews() {
4399             mShowAutofillDialogCalled = false;
4400         }
4401 
checkViewState(AutofillId id)4402         void checkViewState(AutofillId id) {
4403             if (mHasNewTrackedView) {
4404                 return;
4405             }
4406             // First one new tracks view
4407             mIsFillRequested.set(false);
4408             mHasNewTrackedView = true;
4409         }
4410     }
4411 
4412     /**
4413      * Callback for autofill related events.
4414      *
4415      * <p>Typically used for applications that display their own "auto-complete" views, so they can
4416      * enable / disable such views when the autofill UI is shown / hidden.
4417      */
4418     public abstract static class AutofillCallback {
4419 
4420         /** @hide */
4421         @IntDef(prefix = { "EVENT_INPUT_" }, value = {
4422                 EVENT_INPUT_SHOWN,
4423                 EVENT_INPUT_HIDDEN,
4424                 EVENT_INPUT_UNAVAILABLE
4425         })
4426         @Retention(RetentionPolicy.SOURCE)
4427         public @interface AutofillEventType {}
4428 
4429         /**
4430          * The autofill input UI associated with the view was shown.
4431          *
4432          * <p>If the view provides its own auto-complete UI and its currently shown, it
4433          * should be hidden upon receiving this event.
4434          */
4435         public static final int EVENT_INPUT_SHOWN = 1;
4436 
4437         /**
4438          * The autofill input UI associated with the view was hidden.
4439          *
4440          * <p>If the view provides its own auto-complete UI that was hidden upon a
4441          * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
4442          */
4443         public static final int EVENT_INPUT_HIDDEN = 2;
4444 
4445         /**
4446          * The autofill input UI associated with the view isn't shown because
4447          * autofill is not available.
4448          *
4449          * <p>If the view provides its own auto-complete UI but was not displaying it
4450          * to avoid flickering, it could shown it upon receiving this event.
4451          */
4452         public static final int EVENT_INPUT_UNAVAILABLE = 3;
4453 
4454         /**
4455          * Called after a change in the autofill state associated with a view.
4456          *
4457          * @param view view associated with the change.
4458          *
4459          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
4460          */
onAutofillEvent(@onNull View view, @AutofillEventType int event)4461         public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
4462         }
4463 
4464         /**
4465          * Called after a change in the autofill state associated with a virtual view.
4466          *
4467          * @param view parent view associated with the change.
4468          * @param virtualId id identifying the virtual child inside the parent view.
4469          *
4470          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
4471          */
onAutofillEvent(@onNull View view, int virtualId, @AutofillEventType int event)4472         public void onAutofillEvent(@NonNull View view, int virtualId,
4473                 @AutofillEventType int event) {
4474         }
4475     }
4476 
4477     private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
4478         private final WeakReference<AutofillManager> mAfm;
4479 
AutofillManagerClient(AutofillManager autofillManager)4480         private AutofillManagerClient(AutofillManager autofillManager) {
4481             mAfm = new WeakReference<>(autofillManager);
4482         }
4483 
4484         @Override
setState(int flags)4485         public void setState(int flags) {
4486             final AutofillManager afm = mAfm.get();
4487             if (afm != null) {
4488                 afm.post(() -> afm.setState(flags));
4489             }
4490         }
4491 
4492         @Override
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)4493         public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
4494                 boolean hideHighlight) {
4495             final AutofillManager afm = mAfm.get();
4496             if (afm != null) {
4497                 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
4498             }
4499         }
4500 
4501         @Override
onGetCredentialResponse(int sessionId, AutofillId id, GetCredentialResponse response)4502         public void onGetCredentialResponse(int sessionId, AutofillId id,
4503                 GetCredentialResponse response) {
4504             final AutofillManager afm = mAfm.get();
4505             if (afm != null) {
4506                 afm.post(() -> afm.onGetCredentialResponse(sessionId, id, response));
4507             }
4508         }
4509 
4510         @Override
onGetCredentialException(int sessionId, AutofillId id, String errorType, String errorMsg)4511         public void onGetCredentialException(int sessionId, AutofillId id,
4512                 String errorType, String errorMsg) {
4513             final AutofillManager afm = mAfm.get();
4514             if (afm != null) {
4515                 afm.post(() -> afm.onGetCredentialException(sessionId, id, errorType, errorMsg));
4516             }
4517         }
4518 
4519         @Override
autofillContent(int sessionId, AutofillId id, ClipData content)4520         public void autofillContent(int sessionId, AutofillId id, ClipData content) {
4521             final AutofillManager afm = mAfm.get();
4522             if (afm != null) {
4523                 afm.post(() -> afm.autofillContent(sessionId, id, content));
4524             }
4525         }
4526 
4527         @Override
authenticate(int sessionId, int authenticationId, IntentSender intent, Intent fillInIntent, boolean authenticateInline)4528         public void authenticate(int sessionId, int authenticationId, IntentSender intent,
4529                 Intent fillInIntent, boolean authenticateInline) {
4530             final AutofillManager afm = mAfm.get();
4531             if (afm != null) {
4532                 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
4533                         authenticateInline));
4534             }
4535         }
4536 
4537         @Override
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)4538         public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
4539                 Rect anchorBounds, IAutofillWindowPresenter presenter) {
4540             final AutofillManager afm = mAfm.get();
4541             if (afm != null) {
4542                 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
4543                         presenter));
4544             }
4545         }
4546 
4547         @Override
requestHideFillUi(int sessionId, AutofillId id)4548         public void requestHideFillUi(int sessionId, AutofillId id) {
4549             final AutofillManager afm = mAfm.get();
4550             if (afm != null) {
4551                 afm.post(() -> afm.requestHideFillUi(id, false));
4552             }
4553         }
4554 
4555         @Override
requestHideFillUiWhenDestroyed(int sessionId, AutofillId id)4556         public void requestHideFillUiWhenDestroyed(int sessionId, AutofillId id) {
4557             final AutofillManager afm = mAfm.get();
4558             if (afm != null) {
4559                 afm.post(() -> afm.requestHideFillUi(id, true));
4560             }
4561         }
4562 
4563         @Override
notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState)4564         public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
4565             final AutofillManager afm = mAfm.get();
4566             if (afm != null) {
4567                 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
4568             }
4569         }
4570 
4571         @Override
notifyFillUiShown(int sessionId, AutofillId id)4572         public void notifyFillUiShown(int sessionId, AutofillId id) {
4573             final AutofillManager afm = mAfm.get();
4574             if (afm != null) {
4575                 afm.post(
4576                         () -> afm.notifyCallback(
4577                                 sessionId, id, AutofillCallback.EVENT_INPUT_SHOWN));
4578             }
4579         }
4580 
4581         @Override
notifyFillUiHidden(int sessionId, AutofillId id)4582         public void notifyFillUiHidden(int sessionId, AutofillId id) {
4583             final AutofillManager afm = mAfm.get();
4584             if (afm != null) {
4585                 afm.post(
4586                         () -> afm.notifyCallback(
4587                                 sessionId, id, AutofillCallback.EVENT_INPUT_HIDDEN));
4588             }
4589         }
4590 
4591         @Override
notifyDisableAutofill(long disableDuration, ComponentName componentName)4592         public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
4593                 throws RemoteException {
4594             final AutofillManager afm = mAfm.get();
4595             if (afm != null) {
4596                 afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
4597             }
4598         }
4599 
4600         @Override
dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen)4601         public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
4602             final AutofillManager afm = mAfm.get();
4603             if (afm != null) {
4604                 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
4605             }
4606         }
4607 
4608         @Override
startIntentSender(IntentSender intentSender, Intent intent)4609         public void startIntentSender(IntentSender intentSender, Intent intent) {
4610             final AutofillManager afm = mAfm.get();
4611             if (afm != null) {
4612                 afm.post(() -> {
4613                     try {
4614                         Bundle options = ActivityOptions.makeBasic()
4615                                 .setPendingIntentBackgroundActivityStartMode(
4616                                         ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
4617                                 .toBundle();
4618                         afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0, options);
4619                     } catch (IntentSender.SendIntentException e) {
4620                         Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
4621                     }
4622                 });
4623             }
4624         }
4625 
4626         @Override
setTrackedViews(int sessionId, AutofillId[] ids, boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds, AutofillId saveTriggerId)4627         public void setTrackedViews(int sessionId, AutofillId[] ids,
4628                 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
4629                 AutofillId saveTriggerId) {
4630             final AutofillManager afm = mAfm.get();
4631             if (afm != null) {
4632                 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
4633                         saveOnFinish, fillableIds, saveTriggerId));
4634             }
4635         }
4636 
4637         @Override
setSaveUiState(int sessionId, boolean shown)4638         public void setSaveUiState(int sessionId, boolean shown) {
4639             final AutofillManager afm = mAfm.get();
4640             if (afm != null) {
4641                 afm.post(() -> afm.setSaveUiState(sessionId, shown));
4642             }
4643         }
4644 
4645         @Override
setSessionFinished(int newState, List<AutofillId> autofillableIds)4646         public void setSessionFinished(int newState, List<AutofillId> autofillableIds) {
4647             final AutofillManager afm = mAfm.get();
4648             if (afm != null) {
4649                 afm.post(() -> afm.setSessionFinished(newState, autofillableIds));
4650             }
4651         }
4652 
4653         @Override
getAugmentedAutofillClient(IResultReceiver result)4654         public void getAugmentedAutofillClient(IResultReceiver result) {
4655             final AutofillManager afm = mAfm.get();
4656             if (afm != null) {
4657                 afm.post(() -> afm.getAugmentedAutofillClient(result));
4658             }
4659         }
4660 
4661         @Override
requestShowSoftInput(@onNull AutofillId id)4662         public void requestShowSoftInput(@NonNull AutofillId id) {
4663             final AutofillManager afm = mAfm.get();
4664             if (afm != null) {
4665                 afm.post(() -> afm.requestShowSoftInput(id));
4666             }
4667         }
4668 
4669         @Override
notifyFillDialogTriggerIds(List<AutofillId> ids)4670         public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
4671             final AutofillManager afm = mAfm.get();
4672             if (afm != null) {
4673                 afm.post(() -> afm.setFillDialogTriggerIds(ids));
4674             }
4675         }
4676     }
4677 
4678     private static final class AugmentedAutofillManagerClient
4679             extends IAugmentedAutofillManagerClient.Stub {
4680         private final WeakReference<AutofillManager> mAfm;
4681 
AugmentedAutofillManagerClient(AutofillManager autofillManager)4682         private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
4683             mAfm = new WeakReference<>(autofillManager);
4684         }
4685 
4686         @Nullable
4687         @Override
getViewNodeParcelable(@onNull AutofillId id)4688         public ViewNodeParcelable getViewNodeParcelable(@NonNull AutofillId id) {
4689             final AutofillManager afm = mAfm.get();
4690             if (afm == null) return null;
4691 
4692             final View view = getView(afm, id);
4693             if (view == null) {
4694                 Log.w(TAG, "getViewNodeParcelable(" + id + "): could not find view");
4695                 return null;
4696             }
4697             final ViewRootImpl root = view.getViewRootImpl();
4698             if (root != null
4699                     && (root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) == 0) {
4700                 ViewNodeBuilder viewStructure = new ViewNodeBuilder();
4701                 viewStructure.setAutofillId(view.getAutofillId());
4702                 view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
4703                 // TODO(b/141703532): We don't call View#onProvideAutofillVirtualStructure for
4704                 //  efficiency reason. But this also means we will return null for virtual views
4705                 //  for now. We will add a new API to fetch the view node info of the virtual
4706                 //  child view.
4707                 ViewNode viewNode = viewStructure.getViewNode();
4708                 if (viewNode != null && id.equals(viewNode.getAutofillId())) {
4709                     return new ViewNodeParcelable(viewNode);
4710                 }
4711             }
4712             return null;
4713         }
4714 
4715         @Override
getViewCoordinates(@onNull AutofillId id)4716         public Rect getViewCoordinates(@NonNull AutofillId id) {
4717             final AutofillManager afm = mAfm.get();
4718             if (afm == null) return null;
4719 
4720             final View view = getView(afm, id);
4721             if (view == null) {
4722                 return null;
4723             }
4724             final Rect windowVisibleDisplayFrame = new Rect();
4725             view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
4726             final int[] location = new int[2];
4727             view.getLocationOnScreen(location);
4728             final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
4729                     location[0] + view.getWidth(),
4730                     location[1] - windowVisibleDisplayFrame.top + view.getHeight());
4731             if (sVerbose) {
4732                 Log.v(TAG, "Coordinates for " + id + ": " + rect);
4733             }
4734             return rect;
4735         }
4736 
4737         @Override
autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values, boolean hideHighlight)4738         public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
4739                 boolean hideHighlight) {
4740             final AutofillManager afm = mAfm.get();
4741             if (afm != null) {
4742                 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
4743             }
4744         }
4745 
4746         @Override
requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter)4747         public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
4748                 Rect anchorBounds, IAutofillWindowPresenter presenter) {
4749             final AutofillManager afm = mAfm.get();
4750             if (afm != null) {
4751                 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
4752                         presenter));
4753             }
4754         }
4755 
4756         @Override
requestHideFillUi(int sessionId, AutofillId id)4757         public void requestHideFillUi(int sessionId, AutofillId id) {
4758             final AutofillManager afm = mAfm.get();
4759             if (afm != null) {
4760                 afm.post(() -> afm.requestHideFillUi(id, false));
4761             }
4762         }
4763 
4764         @Override
requestAutofill(int sessionId, AutofillId id)4765         public boolean requestAutofill(int sessionId, AutofillId id) {
4766             final AutofillManager afm = mAfm.get();
4767             if (afm == null || afm.mSessionId != sessionId) {
4768                 if (sDebug) {
4769                     Slog.d(TAG, "Autofill not available or sessionId doesn't match");
4770                 }
4771                 return false;
4772             }
4773             final View view = getView(afm, id);
4774             if (view == null || !view.isFocused()) {
4775                 if (sDebug) {
4776                     Slog.d(TAG, "View not available or is not on focus");
4777                 }
4778                 return false;
4779             }
4780             if (sVerbose) {
4781                 Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
4782             }
4783             afm.post(() -> afm.requestAutofillFromNewSession(view));
4784             return true;
4785         }
4786 
4787         @Nullable
getView(@onNull AutofillManager afm, @NonNull AutofillId id)4788         private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
4789             final AutofillClient client = afm.getClient();
4790             if (client == null) {
4791                 Log.w(TAG, "getView(" + id + "): no autofill client");
4792                 return null;
4793             }
4794             View view = client.autofillClientFindViewByAutofillIdTraversal(id);
4795             if (view == null) {
4796                 Log.w(TAG, "getView(" + id + "): could not find view");
4797             }
4798             return view;
4799         }
4800     }
4801 }
4802