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