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