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