AutofillManager.java revision 7f33cd350be4278ce5d4ef460c11e4dbaf9c473b
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.annotation.IntDef;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentSender;
29import android.graphics.Rect;
30import android.metrics.LogMaker;
31import android.os.Bundle;
32import android.os.Parcelable;
33import android.os.RemoteException;
34import android.service.autofill.AutofillService;
35import android.service.autofill.FillEventHistory;
36import android.util.ArrayMap;
37import android.util.ArraySet;
38import android.util.Log;
39import android.util.SparseArray;
40import android.view.View;
41
42import com.android.internal.annotations.GuardedBy;
43import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.nano.MetricsProto;
45
46import java.lang.annotation.Retention;
47import java.lang.annotation.RetentionPolicy;
48import java.lang.ref.WeakReference;
49import java.util.ArrayList;
50import java.util.List;
51import java.util.Objects;
52
53/**
54 * App entry point to the Autofill Framework.
55 *
56 * <p>It is safe to call into this from any thread.
57 */
58public final class AutofillManager {
59
60    private static final String TAG = "AutofillManager";
61
62    /**
63     * Intent extra: The assist structure which captures the filled screen.
64     *
65     * <p>
66     * Type: {@link android.app.assist.AssistStructure}
67     */
68    public static final String EXTRA_ASSIST_STRUCTURE =
69            "android.view.autofill.extra.ASSIST_STRUCTURE";
70
71    /**
72     * Intent extra: The result of an authentication operation. It is
73     * either a fully populated {@link android.service.autofill.FillResponse}
74     * or a fully populated {@link android.service.autofill.Dataset} if
75     * a response or a dataset is being authenticated respectively.
76     *
77     * <p>
78     * Type: {@link android.service.autofill.FillResponse} or a
79     * {@link android.service.autofill.Dataset}
80     */
81    public static final String EXTRA_AUTHENTICATION_RESULT =
82            "android.view.autofill.extra.AUTHENTICATION_RESULT";
83
84    /**
85     * Intent extra: The optional extras provided by the
86     * {@link android.service.autofill.AutofillService}.
87     *
88     * <p>For example, when the service responds to a {@link
89     * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
90     * a {@code FillResponse} that requires authentication, the Intent that launches the
91     * service authentication will contain the Bundle set by
92     * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
93     *
94     * <p>
95     * Type: {@link android.os.Bundle}
96     */
97    public static final String EXTRA_CLIENT_STATE =
98            "android.view.autofill.extra.CLIENT_STATE";
99
100    static final String SESSION_ID_TAG = "android:sessionId";
101    static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
102
103    /** @hide */ public static final int ACTION_START_SESSION = 1;
104    /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
105    /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
106    /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
107
108
109    /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
110    /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
111    /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
112
113    /** Which bits in an authentication id are used for the dataset id */
114    private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
115    /** How many bits in an authentication id are used for the dataset id */
116    private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
117    /** @hide The index for an undefined data set */
118    public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
119
120    /**
121     * Makes an authentication id from a request id and a dataset id.
122     *
123     * @param requestId The request id.
124     * @param datasetId The dataset id.
125     * @return The authentication id.
126     * @hide
127     */
128    public static int makeAuthenticationId(int requestId, int datasetId) {
129        return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
130                | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
131    }
132
133    /**
134     * Gets the request id from an authentication id.
135     *
136     * @param authRequestId The authentication id.
137     * @return The request id.
138     * @hide
139     */
140    public static int getRequestIdFromAuthenticationId(int authRequestId) {
141        return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
142    }
143
144    /**
145     * Gets the dataset id from an authentication id.
146     *
147     * @param authRequestId The authentication id.
148     * @return The dataset id.
149     * @hide
150     */
151    public static int getDatasetIdFromAuthenticationId(int authRequestId) {
152        return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
153    }
154
155    private final MetricsLogger mMetricsLogger = new MetricsLogger();
156
157    /**
158     * There is currently no session running.
159     * {@hide}
160     */
161    public static final int NO_SESSION = Integer.MIN_VALUE;
162
163    private final IAutoFillManager mService;
164
165    private final Object mLock = new Object();
166
167    @GuardedBy("mLock")
168    private IAutoFillManagerClient mServiceClient;
169
170    @GuardedBy("mLock")
171    private AutofillCallback mCallback;
172
173    private final Context mContext;
174
175    @GuardedBy("mLock")
176    private int mSessionId = NO_SESSION;
177
178    @GuardedBy("mLock")
179    private boolean mEnabled;
180
181    /** If a view changes to this mapping the autofill operation was successful */
182    @GuardedBy("mLock")
183    @Nullable private ParcelableMap mLastAutofilledData;
184
185    /** If view tracking is enabled, contains the tracking state */
186    @GuardedBy("mLock")
187    @Nullable private TrackedViews mTrackedViews;
188
189    /** @hide */
190    public interface AutofillClient {
191        /**
192         * Asks the client to start an authentication flow.
193         *
194         * @param authenticationId A unique id of the authentication operation.
195         * @param intent The authentication intent.
196         * @param fillInIntent The authentication fill-in intent.
197         */
198        void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
199                Intent fillInIntent);
200
201        /**
202         * Tells the client this manager has state to be reset.
203         */
204        void autofillCallbackResetableStateAvailable();
205
206        /**
207         * Request showing the autofill UI.
208         *
209         * @param anchor The real view the UI needs to anchor to.
210         * @param width The width of the fill UI content.
211         * @param height The height of the fill UI content.
212         * @param virtualBounds The bounds of the virtual decendant of the anchor.
213         * @param presenter The presenter that controls the fill UI window.
214         * @return Whether the UI was shown.
215         */
216        boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
217                @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
218
219        /**
220         * Request hiding the autofill UI.
221         *
222         * @return Whether the UI was hidden.
223         */
224        boolean autofillCallbackRequestHideFillUi();
225
226        /**
227         * Checks if views are currently attached and visible.
228         *
229         * @return And array with {@code true} iff the view is attached or visible
230         */
231        @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
232
233        /**
234         * Checks is the client is currently visible as understood by autofill.
235         *
236         * @return {@code true} if the client is currently visible
237         */
238        boolean isVisibleForAutofill();
239
240        /**
241         * Find views by traversing the hierarchies of the client.
242         *
243         * @param viewIds The accessibility ids of the views to find
244         *
245         * @return And array containing the views, or {@code null} if not found
246         */
247        @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
248    }
249
250    /**
251     * @hide
252     */
253    public AutofillManager(Context context, IAutoFillManager service) {
254        mContext = context;
255        mService = service;
256    }
257
258    /**
259     * Restore state after activity lifecycle
260     *
261     * @param savedInstanceState The state to be restored
262     *
263     * {@hide}
264     */
265    public void onCreate(Bundle savedInstanceState) {
266        if (!hasAutofillFeature()) {
267            return;
268        }
269        synchronized (mLock) {
270            mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
271
272            if (mSessionId != NO_SESSION) {
273                Log.w(TAG, "New session was started before onCreate()");
274                return;
275            }
276
277            mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
278
279            if (mSessionId != NO_SESSION) {
280                ensureServiceClientAddedIfNeededLocked();
281
282                final AutofillClient client = getClientLocked();
283                if (client != null) {
284                    try {
285                        final boolean sessionWasRestored = mService.restoreSession(mSessionId,
286                                mContext.getActivityToken(), mServiceClient.asBinder());
287
288                        if (!sessionWasRestored) {
289                            Log.w(TAG, "Session " + mSessionId + " could not be restored");
290                            mSessionId = NO_SESSION;
291                        } else {
292                            if (sDebug) {
293                                Log.d(TAG, "session " + mSessionId + " was restored");
294                            }
295
296                            client.autofillCallbackResetableStateAvailable();
297                        }
298                    } catch (RemoteException e) {
299                        Log.e(TAG, "Could not figure out if there was an autofill session", e);
300                    }
301                }
302            }
303        }
304    }
305
306    /**
307     * Called once the client becomes visible.
308     *
309     * @see AutofillClient#isVisibleForAutofill()
310     *
311     * {@hide}
312     */
313    public void onVisibleForAutofill() {
314        synchronized (mLock) {
315            if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
316                mTrackedViews.onVisibleForAutofillLocked();
317            }
318        }
319    }
320
321    /**
322     * Save state before activity lifecycle
323     *
324     * @param outState Place to store the state
325     *
326     * {@hide}
327     */
328    public void onSaveInstanceState(Bundle outState) {
329        if (!hasAutofillFeature()) {
330            return;
331        }
332        synchronized (mLock) {
333            if (mSessionId != NO_SESSION) {
334                outState.putInt(SESSION_ID_TAG, mSessionId);
335            }
336
337            if (mLastAutofilledData != null) {
338                outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
339            }
340        }
341    }
342
343    /**
344     * Checks whether autofill is enabled for the current user.
345     *
346     * <p>Typically used to determine whether the option to explicitly request autofill should
347     * be offered - see {@link #requestAutofill(View)}.
348     *
349     * @return whether autofill is enabled for the current user.
350     */
351    public boolean isEnabled() {
352        if (!hasAutofillFeature()) {
353            return false;
354        }
355        synchronized (mLock) {
356            ensureServiceClientAddedIfNeededLocked();
357            return mEnabled;
358        }
359    }
360
361    /**
362     * Should always be called from {@link AutofillService#getFillEventHistory()}.
363     *
364     * @hide
365     */
366    @Nullable public FillEventHistory getFillEventHistory() {
367        try {
368            return mService.getFillEventHistory();
369        } catch (RemoteException e) {
370            e.rethrowFromSystemServer();
371            return null;
372        }
373    }
374
375    /**
376     * Explicitly requests a new autofill context.
377     *
378     * <p>Normally, the autofill context is automatically started when autofillable views are
379     * focused, but this method should be used in the cases where it must be explicitly requested,
380     * like a view that provides a contextual menu allowing users to autofill the activity.
381     *
382     * @param view view requesting the new autofill context.
383     */
384    public void requestAutofill(@NonNull View view) {
385        notifyViewEntered(view, FLAG_MANUAL_REQUEST);
386    }
387
388    /**
389     * Explicitly requests a new autofill context for virtual views.
390     *
391     * <p>Normally, the autofill context is automatically started when autofillable views are
392     * focused, but this method should be used in the cases where it must be explicitly requested,
393     * like a virtual view that provides a contextual menu allowing users to autofill the activity.
394     *
395     * @param view the {@link View} whose descendant is the virtual view.
396     * @param childId id identifying the virtual child inside the view.
397     * @param bounds child boundaries, relative to the top window.
398     */
399    public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
400        notifyViewEntered(view, childId, bounds, FLAG_MANUAL_REQUEST);
401    }
402
403    /**
404     * Called when a {@link View} that supports autofill is entered.
405     *
406     * @param view {@link View} that was entered.
407     */
408    public void notifyViewEntered(@NonNull View view) {
409        notifyViewEntered(view, 0);
410    }
411
412    private void notifyViewEntered(@NonNull View view, int flags) {
413        if (!hasAutofillFeature()) {
414            return;
415        }
416        AutofillCallback callback = null;
417        synchronized (mLock) {
418            ensureServiceClientAddedIfNeededLocked();
419
420            if (!mEnabled) {
421                if (mCallback != null) {
422                    callback = mCallback;
423                }
424            } else {
425                final AutofillId id = getAutofillId(view);
426                final AutofillValue value = view.getAutofillValue();
427
428                if (mSessionId == NO_SESSION) {
429                    // Starts new session.
430                    startSessionLocked(id, null, value, flags);
431                } else {
432                    // Update focus on existing session.
433                    updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
434                }
435            }
436        }
437
438        if (callback != null) {
439            mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
440        }
441    }
442
443    /**
444     * Called when a {@link View} that supports autofill is exited.
445     *
446     * @param view {@link View} that was exited.
447     */
448    public void notifyViewExited(@NonNull View view) {
449        if (!hasAutofillFeature()) {
450            return;
451        }
452        synchronized (mLock) {
453            ensureServiceClientAddedIfNeededLocked();
454
455            if (mEnabled && mSessionId != NO_SESSION) {
456                final AutofillId id = getAutofillId(view);
457
458                // Update focus on existing session.
459                updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
460            }
461        }
462    }
463
464    /**
465     * Called when a {@link View view's} visibility changes.
466     *
467     * @param view {@link View} that was exited.
468     * @param isVisible visible if the view is visible in the view hierarchy.
469     *
470     * @hide
471     */
472    public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
473        synchronized (mLock) {
474            if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
475                mTrackedViews.notifyViewVisibilityChange(view, isVisible);
476            }
477        }
478    }
479
480    /**
481     * Called when a virtual view that supports autofill is entered.
482     *
483     * @param view the {@link View} whose descendant is the virtual view.
484     * @param childId id identifying the virtual child inside the view.
485     * @param bounds child boundaries, relative to the top window.
486     */
487    public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
488        notifyViewEntered(view, childId, bounds, 0);
489    }
490
491    private void notifyViewEntered(View view, int childId, Rect bounds, int flags) {
492        if (!hasAutofillFeature()) {
493            return;
494        }
495        AutofillCallback callback = null;
496        synchronized (mLock) {
497            ensureServiceClientAddedIfNeededLocked();
498
499            if (!mEnabled) {
500                if (mCallback != null) {
501                    callback = mCallback;
502                }
503            } else {
504                final AutofillId id = getAutofillId(view, childId);
505
506                if (mSessionId == NO_SESSION) {
507                    // Starts new session.
508                    startSessionLocked(id, bounds, null, flags);
509                } else {
510                    // Update focus on existing session.
511                    updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
512                }
513            }
514        }
515
516        if (callback != null) {
517            callback.onAutofillEvent(view, childId,
518                    AutofillCallback.EVENT_INPUT_UNAVAILABLE);
519        }
520    }
521
522    /**
523     * Called when a virtual view that supports autofill is exited.
524     *
525     * @param view the {@link View} whose descendant is the virtual view.
526     * @param childId id identifying the virtual child inside the view.
527     */
528    public void notifyViewExited(@NonNull View view, int childId) {
529        if (!hasAutofillFeature()) {
530            return;
531        }
532        synchronized (mLock) {
533            ensureServiceClientAddedIfNeededLocked();
534
535            if (mEnabled && mSessionId != NO_SESSION) {
536                final AutofillId id = getAutofillId(view, childId);
537
538                // Update focus on existing session.
539                updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
540            }
541        }
542    }
543
544    /**
545     * Called to indicate the value of an autofillable {@link View} changed.
546     *
547     * @param view view whose value changed.
548     */
549    public void notifyValueChanged(View view) {
550        if (!hasAutofillFeature()) {
551            return;
552        }
553        AutofillId id = null;
554        boolean valueWasRead = false;
555        AutofillValue value = null;
556
557        synchronized (mLock) {
558            // If the session is gone some fields might still be highlighted, hence we have to
559            // remove the isAutofilled property even if no sessions are active.
560            if (mLastAutofilledData == null) {
561                view.setAutofilled(false);
562            } else {
563                id = getAutofillId(view);
564                if (mLastAutofilledData.containsKey(id)) {
565                    value = view.getAutofillValue();
566                    valueWasRead = true;
567
568                    if (Objects.equals(mLastAutofilledData.get(id), value)) {
569                        view.setAutofilled(true);
570                    } else {
571                        view.setAutofilled(false);
572                        mLastAutofilledData.remove(id);
573                    }
574                } else {
575                    view.setAutofilled(false);
576                }
577            }
578
579            if (!mEnabled || mSessionId == NO_SESSION) {
580                return;
581            }
582
583            if (id == null) {
584                id = getAutofillId(view);
585            }
586
587            if (!valueWasRead) {
588                value = view.getAutofillValue();
589            }
590
591            updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
592        }
593    }
594
595    /**
596     * Called to indicate the value of an autofillable virtual {@link View} changed.
597     *
598     * @param view the {@link View} whose descendant is the virtual view.
599     * @param childId id identifying the virtual child inside the parent view.
600     * @param value new value of the child.
601     */
602    public void notifyValueChanged(View view, int childId, AutofillValue value) {
603        if (!hasAutofillFeature()) {
604            return;
605        }
606        synchronized (mLock) {
607            if (!mEnabled || mSessionId == NO_SESSION) {
608                return;
609            }
610
611            final AutofillId id = getAutofillId(view, childId);
612            updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
613        }
614    }
615
616    /**
617     * Called to indicate the current autofill context should be commited.
618     *
619     * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
620     * call this method after the form is submitted and another page is rendered.
621     */
622    public void commit() {
623        if (!hasAutofillFeature()) {
624            return;
625        }
626        synchronized (mLock) {
627            if (!mEnabled && mSessionId == NO_SESSION) {
628                return;
629            }
630
631            finishSessionLocked();
632        }
633    }
634
635    /**
636     * Called to indicate the current autofill context should be cancelled.
637     *
638     * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
639     * call this method if the user does not post the form but moves to another form in this page.
640     */
641    public void cancel() {
642        if (!hasAutofillFeature()) {
643            return;
644        }
645        synchronized (mLock) {
646            if (!mEnabled && mSessionId == NO_SESSION) {
647                return;
648            }
649
650            cancelSessionLocked();
651        }
652    }
653
654    /** @hide */
655    public void disableOwnedAutofillServices() {
656        disableAutofillServices();
657    }
658
659    /**
660     * If the app calling this API has enabled autofill services they
661     * will be disabled.
662     */
663    public void disableAutofillServices() {
664        if (!hasAutofillFeature()) {
665            return;
666        }
667        try {
668            mService.disableOwnedAutofillServices(mContext.getUserId());
669        } catch (RemoteException e) {
670            throw e.rethrowFromSystemServer();
671        }
672    }
673
674    /**
675     * Returns {@code true} if the calling application provides a {@link AutofillService} that is
676     * enabled for the current user, or {@code false} otherwise.
677     */
678    public boolean hasEnabledAutofillServices() {
679        if (mService == null) return false;
680
681        try {
682            return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
683        } catch (RemoteException e) {
684            throw e.rethrowFromSystemServer();
685        }
686    }
687
688    /**
689     * Returns {@code true} if Autofill is supported for this user.
690     *
691     * <p>Autofill is typically supported, but it could be unsupported in cases like:
692     * <ol>
693     *     <li>Low-end devices.
694     *     <li>Device policy rules that forbid its usage.
695     * </ol>
696     */
697    public boolean isAutofillSupported() {
698        if (mService == null) return false;
699
700        try {
701            return mService.isServiceSupported(mContext.getUserId());
702        } catch (RemoteException e) {
703            throw e.rethrowFromSystemServer();
704        }
705    }
706
707    private AutofillClient getClientLocked() {
708        if (mContext instanceof AutofillClient) {
709            return (AutofillClient) mContext;
710        }
711        return null;
712    }
713
714    /** @hide */
715    public void onAuthenticationResult(int authenticationId, Intent data) {
716        if (!hasAutofillFeature()) {
717            return;
718        }
719        // TODO: the result code is being ignored, so this method is not reliably
720        // handling the cases where it's not RESULT_OK: it works fine if the service does not
721        // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
722        // service set the extra and returned RESULT_CANCELED...
723
724        if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
725
726        synchronized (mLock) {
727            if (mSessionId == NO_SESSION || data == null) {
728                return;
729            }
730            final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
731            final Bundle responseData = new Bundle();
732            responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
733            try {
734                mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
735                        mContext.getUserId());
736            } catch (RemoteException e) {
737                Log.e(TAG, "Error delivering authentication result", e);
738            }
739        }
740    }
741
742    private static AutofillId getAutofillId(View view) {
743        return new AutofillId(view.getAccessibilityViewId());
744    }
745
746    private static AutofillId getAutofillId(View parent, int childId) {
747        return new AutofillId(parent.getAccessibilityViewId(), childId);
748    }
749
750    private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
751            @NonNull AutofillValue value, int flags) {
752        if (sVerbose) {
753            Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
754                    + ", flags=" + flags);
755        }
756
757        try {
758            mSessionId = mService.startSession(mContext.getActivityToken(),
759                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
760                    mCallback != null, flags, mContext.getOpPackageName());
761            final AutofillClient client = getClientLocked();
762            if (client != null) {
763                client.autofillCallbackResetableStateAvailable();
764            }
765        } catch (RemoteException e) {
766            throw e.rethrowFromSystemServer();
767        }
768    }
769
770    private void finishSessionLocked() {
771        if (sVerbose) Log.v(TAG, "finishSessionLocked()");
772
773        try {
774            mService.finishSession(mSessionId, mContext.getUserId());
775        } catch (RemoteException e) {
776            throw e.rethrowFromSystemServer();
777        }
778
779        mTrackedViews = null;
780        mSessionId = NO_SESSION;
781    }
782
783    private void cancelSessionLocked() {
784        if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
785
786        try {
787            mService.cancelSession(mSessionId, mContext.getUserId());
788        } catch (RemoteException e) {
789            throw e.rethrowFromSystemServer();
790        }
791
792        resetSessionLocked();
793    }
794
795    private void resetSessionLocked() {
796        mSessionId = NO_SESSION;
797        mTrackedViews = null;
798    }
799
800    private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
801            int flags) {
802        if (sVerbose && action != ACTION_VIEW_EXITED) {
803            Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
804                    + ", value=" + value + ", action=" + action + ", flags=" + flags);
805        }
806
807        boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
808
809        try {
810            if (restartIfNecessary) {
811                final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
812                        mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
813                        mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
814                if (newId != mSessionId) {
815                    if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
816                    mSessionId = newId;
817                    final AutofillClient client = getClientLocked();
818                    if (client != null) {
819                        client.autofillCallbackResetableStateAvailable();
820                    }
821                }
822            } else {
823                mService.updateSession(mSessionId, id, bounds, value, action, flags,
824                        mContext.getUserId());
825            }
826
827        } catch (RemoteException e) {
828            throw e.rethrowFromSystemServer();
829        }
830    }
831
832    private void ensureServiceClientAddedIfNeededLocked() {
833        if (getClientLocked() == null) {
834            return;
835        }
836
837        if (mServiceClient == null) {
838            mServiceClient = new AutofillManagerClient(this);
839            try {
840                final int flags = mService.addClient(mServiceClient, mContext.getUserId());
841                mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
842                sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
843                sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
844            } catch (RemoteException e) {
845                throw e.rethrowFromSystemServer();
846            }
847        }
848    }
849
850    /**
851     * Registers a {@link AutofillCallback} to receive autofill events.
852     *
853     * @param callback callback to receive events.
854     */
855    public void registerCallback(@Nullable AutofillCallback callback) {
856        if (!hasAutofillFeature()) {
857            return;
858        }
859        synchronized (mLock) {
860            if (callback == null) return;
861
862            final boolean hadCallback = mCallback != null;
863            mCallback = callback;
864
865            if (!hadCallback) {
866                try {
867                    mService.setHasCallback(mSessionId, mContext.getUserId(), true);
868                } catch (RemoteException e) {
869                    throw e.rethrowFromSystemServer();
870                }
871            }
872        }
873    }
874
875    /**
876     * Unregisters a {@link AutofillCallback} to receive autofill events.
877     *
878     * @param callback callback to stop receiving events.
879     */
880    public void unregisterCallback(@Nullable AutofillCallback callback) {
881        if (!hasAutofillFeature()) {
882            return;
883        }
884        synchronized (mLock) {
885            if (callback == null || mCallback == null || callback != mCallback) return;
886
887            mCallback = null;
888
889            try {
890                mService.setHasCallback(mSessionId, mContext.getUserId(), false);
891            } catch (RemoteException e) {
892                throw e.rethrowFromSystemServer();
893            }
894        }
895    }
896
897    private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
898            Rect anchorBounds, IAutofillWindowPresenter presenter) {
899        final View anchor = findView(id);
900        if (anchor == null) {
901            return;
902        }
903
904        AutofillCallback callback = null;
905        synchronized (mLock) {
906            if (mSessionId == sessionId) {
907                AutofillClient client = getClientLocked();
908
909                if (client != null) {
910                    if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
911                            anchorBounds, presenter) && mCallback != null) {
912                        callback = mCallback;
913                    }
914                }
915            }
916        }
917
918        if (callback != null) {
919            if (id.isVirtual()) {
920                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
921                        AutofillCallback.EVENT_INPUT_SHOWN);
922            } else {
923                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
924            }
925        }
926    }
927
928    private void authenticate(int sessionId, int authenticationId, IntentSender intent,
929            Intent fillInIntent) {
930        synchronized (mLock) {
931            if (sessionId == mSessionId) {
932                AutofillClient client = getClientLocked();
933                if (client != null) {
934                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
935                }
936            }
937        }
938    }
939
940    private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
941        synchronized (mLock) {
942            mEnabled = enabled;
943            if (!mEnabled || resetSession) {
944                // Reset the session state
945                resetSessionLocked();
946            }
947            if (resetClient) {
948                // Reset connection to system
949                mServiceClient = null;
950            }
951        }
952    }
953
954    /**
955     * Sets a view as autofilled if the current value is the {code targetValue}.
956     *
957     * @param view The view that is to be autofilled
958     * @param targetValue The value we want to fill into view
959     */
960    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
961        AutofillValue currentValue = view.getAutofillValue();
962        if (Objects.equals(currentValue, targetValue)) {
963            synchronized (mLock) {
964                if (mLastAutofilledData == null) {
965                    mLastAutofilledData = new ParcelableMap(1);
966                }
967                mLastAutofilledData.put(getAutofillId(view), targetValue);
968            }
969            view.setAutofilled(true);
970        }
971    }
972
973    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
974        synchronized (mLock) {
975            if (sessionId != mSessionId) {
976                return;
977            }
978
979            final AutofillClient client = getClientLocked();
980            if (client == null) {
981                return;
982            }
983
984            final int itemCount = ids.size();
985            int numApplied = 0;
986            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
987            final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
988
989            for (int i = 0; i < itemCount; i++) {
990                final AutofillId id = ids.get(i);
991                final AutofillValue value = values.get(i);
992                final int viewId = id.getViewId();
993                final View view = views[i];
994                if (view == null) {
995                    Log.w(TAG, "autofill(): no View with id " + viewId);
996                    continue;
997                }
998                if (id.isVirtual()) {
999                    if (virtualValues == null) {
1000                        // Most likely there will be just one view with virtual children.
1001                        virtualValues = new ArrayMap<>(1);
1002                    }
1003                    SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1004                    if (valuesByParent == null) {
1005                        // We don't know the size yet, but usually it will be just a few fields...
1006                        valuesByParent = new SparseArray<>(5);
1007                        virtualValues.put(view, valuesByParent);
1008                    }
1009                    valuesByParent.put(id.getVirtualChildId(), value);
1010                } else {
1011                    // Mark the view as to be autofilled with 'value'
1012                    if (mLastAutofilledData == null) {
1013                        mLastAutofilledData = new ParcelableMap(itemCount - i);
1014                    }
1015                    mLastAutofilledData.put(id, value);
1016
1017                    view.autofill(value);
1018
1019                    // Set as autofilled if the values match now, e.g. when the value was updated
1020                    // synchronously.
1021                    // If autofill happens async, the view is set to autofilled in
1022                    // notifyValueChanged.
1023                    setAutofilledIfValuesIs(view, value);
1024
1025                    numApplied++;
1026                }
1027            }
1028
1029            if (virtualValues != null) {
1030                for (int i = 0; i < virtualValues.size(); i++) {
1031                    final View parent = virtualValues.keyAt(i);
1032                    final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1033                    parent.autofill(childrenValues);
1034                    numApplied += childrenValues.size();
1035                }
1036            }
1037
1038            final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1039            log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1040            log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1041                    numApplied);
1042            mMetricsLogger.write(log);
1043        }
1044    }
1045
1046    /**
1047     *  Set the tracked views.
1048     *
1049     * @param trackedIds The views to be tracked
1050     * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
1051     */
1052    private void setTrackedViews(int sessionId, List<AutofillId> trackedIds,
1053            boolean saveOnAllViewsInvisible) {
1054        synchronized (mLock) {
1055            if (mEnabled && mSessionId == sessionId) {
1056                if (saveOnAllViewsInvisible) {
1057                    mTrackedViews = new TrackedViews(trackedIds);
1058                } else {
1059                    mTrackedViews = null;
1060                }
1061            }
1062        }
1063    }
1064
1065    private void requestHideFillUi(int sessionId, AutofillId id) {
1066        final View anchor = findView(id);
1067        if (anchor == null) {
1068            return;
1069        }
1070
1071        AutofillCallback callback = null;
1072        synchronized (mLock) {
1073            // We do not check the session id for two reasons:
1074            // 1. If local and remote session id are off sync the UI would be stuck shown
1075            // 2. There is a race between the user state being destroyed due the fill
1076            //    service being uninstalled and the UI being dismissed.
1077            AutofillClient client = getClientLocked();
1078            if (client != null) {
1079                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1080                    callback = mCallback;
1081                }
1082            }
1083        }
1084
1085        if (callback != null) {
1086            if (id.isVirtual()) {
1087                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1088                        AutofillCallback.EVENT_INPUT_HIDDEN);
1089            } else {
1090                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
1091            }
1092        }
1093    }
1094
1095    private void notifyNoFillUi(int sessionId, AutofillId id) {
1096        final View anchor = findView(id);
1097        if (anchor == null) {
1098            return;
1099        }
1100
1101        AutofillCallback callback = null;
1102        synchronized (mLock) {
1103            if (mSessionId == sessionId && getClientLocked() != null) {
1104                callback = mCallback;
1105            }
1106        }
1107
1108        if (callback != null) {
1109            if (id.isVirtual()) {
1110                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1111                        AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1112            } else {
1113                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1114            }
1115
1116        }
1117    }
1118
1119    /**
1120     * Get an array of viewIds from a List of {@link AutofillId}.
1121     *
1122     * @param autofillIds The autofill ids to convert
1123     *
1124     * @return The array of viewIds.
1125     */
1126    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1127        final int numIds = autofillIds.size();
1128        final int[] viewIds = new int[numIds];
1129        for (int i = 0; i < numIds; i++) {
1130            viewIds[i] = autofillIds.get(i).getViewId();
1131        }
1132
1133        return viewIds;
1134    }
1135
1136    /**
1137     * Find a single view by its id.
1138     *
1139     * @param autofillId The autofill id of the view
1140     *
1141     * @return The view or {@code null} if view was not found
1142     */
1143    private View findView(@NonNull AutofillId autofillId) {
1144        final AutofillClient client = getClientLocked();
1145
1146        if (client == null) {
1147            return null;
1148        }
1149
1150        return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
1151    }
1152
1153    /** @hide */
1154    public boolean hasAutofillFeature() {
1155        return mService != null;
1156    }
1157
1158    /**
1159     * View tracking information. Once all tracked views become invisible the session is finished.
1160     */
1161    private class TrackedViews {
1162        /** Visible tracked views */
1163        @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1164
1165        /** Invisible tracked views */
1166        @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1167
1168        /**
1169         * Check if set is null or value is in set.
1170         *
1171         * @param set   The set or null (== empty set)
1172         * @param value The value that might be in the set
1173         *
1174         * @return {@code true} iff set is not empty and value is in set
1175         */
1176        private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1177            return set != null && set.contains(value);
1178        }
1179
1180        /**
1181         * Add a value to a set. If set is null, create a new set.
1182         *
1183         * @param set        The set or null (== empty set)
1184         * @param valueToAdd The value to add
1185         *
1186         * @return The set including the new value. If set was {@code null}, a set containing only
1187         *         the new value.
1188         */
1189        @NonNull
1190        private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1191            if (set == null) {
1192                set = new ArraySet<>(1);
1193            }
1194
1195            set.add(valueToAdd);
1196
1197            return set;
1198        }
1199
1200        /**
1201         * Remove a value from a set.
1202         *
1203         * @param set           The set or null (== empty set)
1204         * @param valueToRemove The value to remove
1205         *
1206         * @return The set without the removed value. {@code null} if set was null, or is empty
1207         *         after removal.
1208         */
1209        @Nullable
1210        private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1211            if (set == null) {
1212                return null;
1213            }
1214
1215            set.remove(valueToRemove);
1216
1217            if (set.isEmpty()) {
1218                return null;
1219            }
1220
1221            return set;
1222        }
1223
1224        /**
1225         * Set the tracked views.
1226         *
1227         * @param trackedIds The views to be tracked
1228         */
1229        TrackedViews(List<AutofillId> trackedIds) {
1230            mVisibleTrackedIds = null;
1231            mInvisibleTrackedIds = null;
1232
1233            AutofillClient client = getClientLocked();
1234            if (trackedIds != null && client != null) {
1235                final boolean[] isVisible;
1236
1237                if (client.isVisibleForAutofill()) {
1238                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
1239                } else {
1240                    // All false
1241                    isVisible = new boolean[trackedIds.size()];
1242                }
1243
1244                final int numIds = trackedIds.size();
1245                for (int i = 0; i < numIds; i++) {
1246                    final AutofillId id = trackedIds.get(i);
1247
1248                    if (isVisible[i]) {
1249                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1250                    } else {
1251                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1252                    }
1253                }
1254            }
1255
1256            if (sVerbose) {
1257                Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
1258                        + " mVisibleTrackedIds=" + mVisibleTrackedIds
1259                        + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1260            }
1261
1262            if (mVisibleTrackedIds == null) {
1263                finishSessionLocked();
1264            }
1265        }
1266
1267        /**
1268         * Called when a {@link View view's} visibility changes.
1269         *
1270         * @param view {@link View} that was exited.
1271         * @param isVisible visible if the view is visible in the view hierarchy.
1272         */
1273        void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
1274            AutofillId id = getAutofillId(view);
1275            AutofillClient client = getClientLocked();
1276
1277            if (sDebug) {
1278                Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible="
1279                        + isVisible);
1280            }
1281
1282            if (client != null && client.isVisibleForAutofill()) {
1283                if (isVisible) {
1284                    if (isInSet(mInvisibleTrackedIds, id)) {
1285                        mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1286                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1287                    }
1288                } else {
1289                    if (isInSet(mVisibleTrackedIds, id)) {
1290                        mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1291                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1292                    }
1293                }
1294            }
1295
1296            if (mVisibleTrackedIds == null) {
1297                finishSessionLocked();
1298            }
1299        }
1300
1301        /**
1302         * Called once the client becomes visible.
1303         *
1304         * @see AutofillClient#isVisibleForAutofill()
1305         */
1306        void onVisibleForAutofillLocked() {
1307            // The visibility of the views might have changed while the client was not be visible,
1308            // hence update the visibility state for all views.
1309            AutofillClient client = getClientLocked();
1310            ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1311            ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1312            if (client != null) {
1313                if (mInvisibleTrackedIds != null) {
1314                    final ArrayList<AutofillId> orderedInvisibleIds =
1315                            new ArrayList<>(mInvisibleTrackedIds);
1316                    final boolean[] isVisible = client.getViewVisibility(
1317                            getViewIds(orderedInvisibleIds));
1318
1319                    final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1320                    for (int i = 0; i < numInvisibleTrackedIds; i++) {
1321                        final AutofillId id = orderedInvisibleIds.get(i);
1322                        if (isVisible[i]) {
1323                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1324
1325                            if (sDebug) {
1326                                Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
1327                            }
1328                        } else {
1329                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1330                        }
1331                    }
1332                }
1333
1334                if (mVisibleTrackedIds != null) {
1335                    final ArrayList<AutofillId> orderedVisibleIds =
1336                            new ArrayList<>(mVisibleTrackedIds);
1337                    final boolean[] isVisible = client.getViewVisibility(
1338                            getViewIds(orderedVisibleIds));
1339
1340                    final int numVisibleTrackedIds = orderedVisibleIds.size();
1341                    for (int i = 0; i < numVisibleTrackedIds; i++) {
1342                        final AutofillId id = orderedVisibleIds.get(i);
1343
1344                        if (isVisible[i]) {
1345                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1346                        } else {
1347                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1348
1349                            if (sDebug) {
1350                                Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
1351                            }
1352                        }
1353                    }
1354                }
1355
1356                mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1357                mVisibleTrackedIds = updatedVisibleTrackedIds;
1358            }
1359
1360            if (mVisibleTrackedIds == null) {
1361                finishSessionLocked();
1362            }
1363        }
1364    }
1365
1366    /**
1367     * Callback for auto-fill related events.
1368     *
1369     * <p>Typically used for applications that display their own "auto-complete" views, so they can
1370     * enable / disable such views when the auto-fill UI affordance is shown / hidden.
1371     */
1372    public abstract static class AutofillCallback {
1373
1374        /** @hide */
1375        @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1376        @Retention(RetentionPolicy.SOURCE)
1377        public @interface AutofillEventType {}
1378
1379        /**
1380         * The auto-fill input UI affordance associated with the view was shown.
1381         *
1382         * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1383         * should be hidden upon receiving this event.
1384         */
1385        public static final int EVENT_INPUT_SHOWN = 1;
1386
1387        /**
1388         * The auto-fill input UI affordance associated with the view was hidden.
1389         *
1390         * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1391         * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1392         */
1393        public static final int EVENT_INPUT_HIDDEN = 2;
1394
1395        /**
1396         * The auto-fill input UI affordance associated with the view won't be shown because
1397         * autofill is not available.
1398         *
1399         * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1400         * to avoid flickering, it could shown it upon receiving this event.
1401         */
1402        public static final int EVENT_INPUT_UNAVAILABLE = 3;
1403
1404        /**
1405         * Called after a change in the autofill state associated with a view.
1406         *
1407         * @param view view associated with the change.
1408         *
1409         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1410         */
1411        public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1412        }
1413
1414        /**
1415         * Called after a change in the autofill state associated with a virtual view.
1416         *
1417         * @param view parent view associated with the change.
1418         * @param childId id identifying the virtual child inside the parent view.
1419         *
1420         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1421         */
1422        public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
1423        }
1424    }
1425
1426    private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1427        private final WeakReference<AutofillManager> mAfm;
1428
1429        AutofillManagerClient(AutofillManager autofillManager) {
1430            mAfm = new WeakReference<>(autofillManager);
1431        }
1432
1433        @Override
1434        public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
1435            final AutofillManager afm = mAfm.get();
1436            if (afm != null) {
1437                afm.mContext.getMainThreadHandler().post(
1438                        () -> afm.setState(enabled, resetSession, resetClient));
1439            }
1440        }
1441
1442        @Override
1443        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
1444            final AutofillManager afm = mAfm.get();
1445            if (afm != null) {
1446                afm.mContext.getMainThreadHandler().post(
1447                        () -> afm.autofill(sessionId, ids, values));
1448            }
1449        }
1450
1451        @Override
1452        public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1453                Intent fillInIntent) {
1454            final AutofillManager afm = mAfm.get();
1455            if (afm != null) {
1456                afm.mContext.getMainThreadHandler().post(
1457                        () -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
1458            }
1459        }
1460
1461        @Override
1462        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1463                Rect anchorBounds, IAutofillWindowPresenter presenter) {
1464            final AutofillManager afm = mAfm.get();
1465            if (afm != null) {
1466                afm.mContext.getMainThreadHandler().post(
1467                        () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1468                                presenter));
1469            }
1470        }
1471
1472        @Override
1473        public void requestHideFillUi(int sessionId, AutofillId id) {
1474            final AutofillManager afm = mAfm.get();
1475            if (afm != null) {
1476                afm.mContext.getMainThreadHandler().post(
1477                        () -> afm.requestHideFillUi(sessionId, id));
1478            }
1479        }
1480
1481        @Override
1482        public void notifyNoFillUi(int sessionId, AutofillId id) {
1483            final AutofillManager afm = mAfm.get();
1484            if (afm != null) {
1485                afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id));
1486            }
1487        }
1488
1489        @Override
1490        public void startIntentSender(IntentSender intentSender) {
1491            final AutofillManager afm = mAfm.get();
1492            if (afm != null) {
1493                afm.mContext.getMainThreadHandler().post(() -> {
1494                    try {
1495                        afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
1496                    } catch (IntentSender.SendIntentException e) {
1497                        Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1498                    }
1499                });
1500            }
1501        }
1502
1503        @Override
1504        public void setTrackedViews(int sessionId, List<AutofillId> ids,
1505                boolean saveOnAllViewsInvisible) {
1506            final AutofillManager afm = mAfm.get();
1507            if (afm != null) {
1508                afm.mContext.getMainThreadHandler().post(
1509                        () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible)
1510                );
1511            }
1512        }
1513    }
1514}
1515