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