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