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