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