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