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