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