AutofillManager.java revision d86b8fea43ebb6e5c31691b44d8ceb0d8d3c9072
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 accessibility ids of the views to find
250         *
251         * @return And array containing the views (empty if no views found).
252         */
253        @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
254
255        /**
256         * Finds a view by traversing the hierarchies of the client.
257         *
258         * @param viewId The accessibility id of the views to find
259         *
260         * @return The view, or {@code null} if not found
261         */
262        @Nullable View findViewByAccessibilityIdTraversal(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 for this user.
745     *
746     * <p>Autofill is typically supported, but it could be unsupported in cases like:
747     * <ol>
748     *     <li>Low-end devices.
749     *     <li>Device policy rules that forbid its usage.
750     * </ol>
751     */
752    public boolean isAutofillSupported() {
753        if (mService == null) return false;
754
755        try {
756            return mService.isServiceSupported(mContext.getUserId());
757        } catch (RemoteException e) {
758            throw e.rethrowFromSystemServer();
759        }
760    }
761
762    private AutofillClient getClientLocked() {
763        if (mContext instanceof AutofillClient) {
764            return (AutofillClient) mContext;
765        }
766        return null;
767    }
768
769    /** @hide */
770    public void onAuthenticationResult(int authenticationId, Intent data) {
771        if (!hasAutofillFeature()) {
772            return;
773        }
774        // TODO: the result code is being ignored, so this method is not reliably
775        // handling the cases where it's not RESULT_OK: it works fine if the service does not
776        // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
777        // service set the extra and returned RESULT_CANCELED...
778
779        if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
780
781        synchronized (mLock) {
782            if (mSessionId == NO_SESSION || data == null) {
783                return;
784            }
785            final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
786            final Bundle responseData = new Bundle();
787            responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
788            try {
789                mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
790                        mContext.getUserId());
791            } catch (RemoteException e) {
792                Log.e(TAG, "Error delivering authentication result", e);
793            }
794        }
795    }
796
797    private static AutofillId getAutofillId(View view) {
798        return new AutofillId(view.getAccessibilityViewId());
799    }
800
801    private static AutofillId getAutofillId(View parent, int virtualId) {
802        return new AutofillId(parent.getAccessibilityViewId(), virtualId);
803    }
804
805    private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
806            @NonNull AutofillValue value, int flags) {
807        if (sVerbose) {
808            Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
809                    + ", flags=" + flags);
810        }
811
812        try {
813            mSessionId = mService.startSession(mContext.getActivityToken(),
814                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
815                    mCallback != null, flags, mContext.getOpPackageName());
816            final AutofillClient client = getClientLocked();
817            if (client != null) {
818                client.autofillCallbackResetableStateAvailable();
819            }
820        } catch (RemoteException e) {
821            throw e.rethrowFromSystemServer();
822        }
823    }
824
825    private void finishSessionLocked() {
826        if (sVerbose) Log.v(TAG, "finishSessionLocked()");
827
828        try {
829            mService.finishSession(mSessionId, mContext.getUserId());
830        } catch (RemoteException e) {
831            throw e.rethrowFromSystemServer();
832        }
833
834        mTrackedViews = null;
835        mSessionId = NO_SESSION;
836    }
837
838    private void cancelSessionLocked() {
839        if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
840
841        try {
842            mService.cancelSession(mSessionId, mContext.getUserId());
843        } catch (RemoteException e) {
844            throw e.rethrowFromSystemServer();
845        }
846
847        resetSessionLocked();
848    }
849
850    private void resetSessionLocked() {
851        mSessionId = NO_SESSION;
852        mTrackedViews = null;
853    }
854
855    private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
856            int flags) {
857        if (sVerbose && action != ACTION_VIEW_EXITED) {
858            Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
859                    + ", value=" + value + ", action=" + action + ", flags=" + flags);
860        }
861
862        boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
863
864        try {
865            if (restartIfNecessary) {
866                final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
867                        mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
868                        mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
869                if (newId != mSessionId) {
870                    if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
871                    mSessionId = newId;
872                    final AutofillClient client = getClientLocked();
873                    if (client != null) {
874                        client.autofillCallbackResetableStateAvailable();
875                    }
876                }
877            } else {
878                mService.updateSession(mSessionId, id, bounds, value, action, flags,
879                        mContext.getUserId());
880            }
881
882        } catch (RemoteException e) {
883            throw e.rethrowFromSystemServer();
884        }
885    }
886
887    private void ensureServiceClientAddedIfNeededLocked() {
888        if (getClientLocked() == null) {
889            return;
890        }
891
892        if (mServiceClient == null) {
893            mServiceClient = new AutofillManagerClient(this);
894            try {
895                final int flags = mService.addClient(mServiceClient, mContext.getUserId());
896                mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
897                sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
898                sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
899            } catch (RemoteException e) {
900                throw e.rethrowFromSystemServer();
901            }
902        }
903    }
904
905    /**
906     * Registers a {@link AutofillCallback} to receive autofill events.
907     *
908     * @param callback callback to receive events.
909     */
910    public void registerCallback(@Nullable AutofillCallback callback) {
911        if (!hasAutofillFeature()) {
912            return;
913        }
914        synchronized (mLock) {
915            if (callback == null) return;
916
917            final boolean hadCallback = mCallback != null;
918            mCallback = callback;
919
920            if (!hadCallback) {
921                try {
922                    mService.setHasCallback(mSessionId, mContext.getUserId(), true);
923                } catch (RemoteException e) {
924                    throw e.rethrowFromSystemServer();
925                }
926            }
927        }
928    }
929
930    /**
931     * Unregisters a {@link AutofillCallback} to receive autofill events.
932     *
933     * @param callback callback to stop receiving events.
934     */
935    public void unregisterCallback(@Nullable AutofillCallback callback) {
936        if (!hasAutofillFeature()) {
937            return;
938        }
939        synchronized (mLock) {
940            if (callback == null || mCallback == null || callback != mCallback) return;
941
942            mCallback = null;
943
944            try {
945                mService.setHasCallback(mSessionId, mContext.getUserId(), false);
946            } catch (RemoteException e) {
947                throw e.rethrowFromSystemServer();
948            }
949        }
950    }
951
952    private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
953            Rect anchorBounds, IAutofillWindowPresenter presenter) {
954        final View anchor = findView(id);
955        if (anchor == null) {
956            return;
957        }
958
959        AutofillCallback callback = null;
960        synchronized (mLock) {
961            if (mSessionId == sessionId) {
962                AutofillClient client = getClientLocked();
963
964                if (client != null) {
965                    if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
966                            anchorBounds, presenter) && mCallback != null) {
967                        callback = mCallback;
968                    }
969                }
970            }
971        }
972
973        if (callback != null) {
974            if (id.isVirtual()) {
975                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
976                        AutofillCallback.EVENT_INPUT_SHOWN);
977            } else {
978                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
979            }
980        }
981    }
982
983    private void authenticate(int sessionId, int authenticationId, IntentSender intent,
984            Intent fillInIntent) {
985        synchronized (mLock) {
986            if (sessionId == mSessionId) {
987                AutofillClient client = getClientLocked();
988                if (client != null) {
989                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
990                }
991            }
992        }
993    }
994
995    private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
996        synchronized (mLock) {
997            mEnabled = enabled;
998            if (!mEnabled || resetSession) {
999                // Reset the session state
1000                resetSessionLocked();
1001            }
1002            if (resetClient) {
1003                // Reset connection to system
1004                mServiceClient = null;
1005            }
1006        }
1007    }
1008
1009    /**
1010     * Sets a view as autofilled if the current value is the {code targetValue}.
1011     *
1012     * @param view The view that is to be autofilled
1013     * @param targetValue The value we want to fill into view
1014     */
1015    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1016        AutofillValue currentValue = view.getAutofillValue();
1017        if (Objects.equals(currentValue, targetValue)) {
1018            synchronized (mLock) {
1019                if (mLastAutofilledData == null) {
1020                    mLastAutofilledData = new ParcelableMap(1);
1021                }
1022                mLastAutofilledData.put(getAutofillId(view), targetValue);
1023            }
1024            view.setAutofilled(true);
1025        }
1026    }
1027
1028    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
1029        synchronized (mLock) {
1030            if (sessionId != mSessionId) {
1031                return;
1032            }
1033
1034            final AutofillClient client = getClientLocked();
1035            if (client == null) {
1036                return;
1037            }
1038
1039            final int itemCount = ids.size();
1040            int numApplied = 0;
1041            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
1042            final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
1043
1044            for (int i = 0; i < itemCount; i++) {
1045                final AutofillId id = ids.get(i);
1046                final AutofillValue value = values.get(i);
1047                final int viewId = id.getViewId();
1048                final View view = views[i];
1049                if (view == null) {
1050                    Log.w(TAG, "autofill(): no View with id " + viewId);
1051                    continue;
1052                }
1053                if (id.isVirtual()) {
1054                    if (virtualValues == null) {
1055                        // Most likely there will be just one view with virtual children.
1056                        virtualValues = new ArrayMap<>(1);
1057                    }
1058                    SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1059                    if (valuesByParent == null) {
1060                        // We don't know the size yet, but usually it will be just a few fields...
1061                        valuesByParent = new SparseArray<>(5);
1062                        virtualValues.put(view, valuesByParent);
1063                    }
1064                    valuesByParent.put(id.getVirtualChildId(), value);
1065                } else {
1066                    // Mark the view as to be autofilled with 'value'
1067                    if (mLastAutofilledData == null) {
1068                        mLastAutofilledData = new ParcelableMap(itemCount - i);
1069                    }
1070                    mLastAutofilledData.put(id, value);
1071
1072                    view.autofill(value);
1073
1074                    // Set as autofilled if the values match now, e.g. when the value was updated
1075                    // synchronously.
1076                    // If autofill happens async, the view is set to autofilled in
1077                    // notifyValueChanged.
1078                    setAutofilledIfValuesIs(view, value);
1079
1080                    numApplied++;
1081                }
1082            }
1083
1084            if (virtualValues != null) {
1085                for (int i = 0; i < virtualValues.size(); i++) {
1086                    final View parent = virtualValues.keyAt(i);
1087                    final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1088                    parent.autofill(childrenValues);
1089                    numApplied += childrenValues.size();
1090                }
1091            }
1092
1093            final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1094            log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1095            log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1096                    numApplied);
1097            mMetricsLogger.write(log);
1098        }
1099    }
1100
1101    /**
1102     *  Set the tracked views.
1103     *
1104     * @param trackedIds The views to be tracked
1105     * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
1106     * @param fillableIds Views that might anchor FillUI.
1107     */
1108    private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1109            boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
1110        synchronized (mLock) {
1111            if (mEnabled && mSessionId == sessionId) {
1112                if (saveOnAllViewsInvisible) {
1113                    mTrackedViews = new TrackedViews(trackedIds);
1114                } else {
1115                    mTrackedViews = null;
1116                }
1117                if (fillableIds != null) {
1118                    if (mFillableIds == null) {
1119                        mFillableIds = new ArraySet<>(fillableIds.length);
1120                    }
1121                    for (AutofillId id : fillableIds) {
1122                        mFillableIds.add(id);
1123                    }
1124                    if (sVerbose) {
1125                        Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1126                                + ", mFillableIds" + mFillableIds);
1127                    }
1128                }
1129            }
1130        }
1131    }
1132
1133    private void requestHideFillUi(AutofillId id) {
1134        final View anchor = findView(id);
1135        if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
1136        if (anchor == null) {
1137            return;
1138        }
1139        requestHideFillUi(id, anchor);
1140    }
1141
1142    private void requestHideFillUi(AutofillId id, View anchor) {
1143
1144        AutofillCallback callback = null;
1145        synchronized (mLock) {
1146            // We do not check the session id for two reasons:
1147            // 1. If local and remote session id are off sync the UI would be stuck shown
1148            // 2. There is a race between the user state being destroyed due the fill
1149            //    service being uninstalled and the UI being dismissed.
1150            AutofillClient client = getClientLocked();
1151            if (client != null) {
1152                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1153                    callback = mCallback;
1154                }
1155            }
1156        }
1157
1158        if (callback != null) {
1159            if (id.isVirtual()) {
1160                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1161                        AutofillCallback.EVENT_INPUT_HIDDEN);
1162            } else {
1163                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
1164            }
1165        }
1166    }
1167
1168    private void notifyNoFillUi(int sessionId, AutofillId id) {
1169        final View anchor = findView(id);
1170        if (anchor == null) {
1171            return;
1172        }
1173
1174        AutofillCallback callback = null;
1175        synchronized (mLock) {
1176            if (mSessionId == sessionId && getClientLocked() != null) {
1177                callback = mCallback;
1178            }
1179        }
1180
1181        if (callback != null) {
1182            if (id.isVirtual()) {
1183                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1184                        AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1185            } else {
1186                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1187            }
1188
1189        }
1190    }
1191
1192    /**
1193     * Get an array of viewIds from a List of {@link AutofillId}.
1194     *
1195     * @param autofillIds The autofill ids to convert
1196     *
1197     * @return The array of viewIds.
1198     */
1199    // TODO: move to Helper as static method
1200    @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1201        final int numIds = autofillIds.length;
1202        final int[] viewIds = new int[numIds];
1203        for (int i = 0; i < numIds; i++) {
1204            viewIds[i] = autofillIds[i].getViewId();
1205        }
1206
1207        return viewIds;
1208    }
1209
1210    // TODO: move to Helper as static method
1211    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1212        final int numIds = autofillIds.size();
1213        final int[] viewIds = new int[numIds];
1214        for (int i = 0; i < numIds; i++) {
1215            viewIds[i] = autofillIds.get(i).getViewId();
1216        }
1217
1218        return viewIds;
1219    }
1220
1221    /**
1222     * Find a single view by its id.
1223     *
1224     * @param autofillId The autofill id of the view
1225     *
1226     * @return The view or {@code null} if view was not found
1227     */
1228    private View findView(@NonNull AutofillId autofillId) {
1229        final AutofillClient client = getClientLocked();
1230
1231        if (client == null) {
1232            return null;
1233        }
1234
1235        return client.findViewByAccessibilityIdTraversal(autofillId.getViewId());
1236    }
1237
1238    /** @hide */
1239    public boolean hasAutofillFeature() {
1240        return mService != null;
1241    }
1242
1243    private void post(Runnable runnable) {
1244        final AutofillClient client = getClientLocked();
1245        if (client == null) {
1246            if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1247            return;
1248        }
1249        client.runOnUiThread(runnable);
1250    }
1251
1252    /**
1253     * View tracking information. Once all tracked views become invisible the session is finished.
1254     */
1255    private class TrackedViews {
1256        /** Visible tracked views */
1257        @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1258
1259        /** Invisible tracked views */
1260        @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1261
1262        /**
1263         * Check if set is null or value is in set.
1264         *
1265         * @param set   The set or null (== empty set)
1266         * @param value The value that might be in the set
1267         *
1268         * @return {@code true} iff set is not empty and value is in set
1269         */
1270        // TODO: move to Helper as static method
1271        private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1272            return set != null && set.contains(value);
1273        }
1274
1275        /**
1276         * Add a value to a set. If set is null, create a new set.
1277         *
1278         * @param set        The set or null (== empty set)
1279         * @param valueToAdd The value to add
1280         *
1281         * @return The set including the new value. If set was {@code null}, a set containing only
1282         *         the new value.
1283         */
1284        // TODO: move to Helper as static method
1285        @NonNull
1286        private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1287            if (set == null) {
1288                set = new ArraySet<>(1);
1289            }
1290
1291            set.add(valueToAdd);
1292
1293            return set;
1294        }
1295
1296        /**
1297         * Remove a value from a set.
1298         *
1299         * @param set           The set or null (== empty set)
1300         * @param valueToRemove The value to remove
1301         *
1302         * @return The set without the removed value. {@code null} if set was null, or is empty
1303         *         after removal.
1304         */
1305        // TODO: move to Helper as static method
1306        @Nullable
1307        private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1308            if (set == null) {
1309                return null;
1310            }
1311
1312            set.remove(valueToRemove);
1313
1314            if (set.isEmpty()) {
1315                return null;
1316            }
1317
1318            return set;
1319        }
1320
1321        /**
1322         * Set the tracked views.
1323         *
1324         * @param trackedIds The views to be tracked
1325         */
1326        TrackedViews(@Nullable AutofillId[] trackedIds) {
1327            final AutofillClient client = getClientLocked();
1328            if (trackedIds != null && client != null) {
1329                final boolean[] isVisible;
1330
1331                if (client.isVisibleForAutofill()) {
1332                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
1333                } else {
1334                    // All false
1335                    isVisible = new boolean[trackedIds.length];
1336                }
1337
1338                final int numIds = trackedIds.length;
1339                for (int i = 0; i < numIds; i++) {
1340                    final AutofillId id = trackedIds[i];
1341
1342                    if (isVisible[i]) {
1343                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1344                    } else {
1345                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1346                    }
1347                }
1348            }
1349
1350            if (sVerbose) {
1351                Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
1352                        + " mVisibleTrackedIds=" + mVisibleTrackedIds
1353                        + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1354            }
1355
1356            if (mVisibleTrackedIds == null) {
1357                finishSessionLocked();
1358            }
1359        }
1360
1361        /**
1362         * Called when a {@link View view's} visibility changes.
1363         *
1364         * @param view {@link View} that was exited.
1365         * @param isVisible visible if the view is visible in the view hierarchy.
1366         */
1367        void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
1368            AutofillId id = getAutofillId(view);
1369            AutofillClient client = getClientLocked();
1370
1371            if (sDebug) {
1372                Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible="
1373                        + isVisible);
1374            }
1375
1376            if (client != null && client.isVisibleForAutofill()) {
1377                if (isVisible) {
1378                    if (isInSet(mInvisibleTrackedIds, id)) {
1379                        mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1380                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1381                    }
1382                } else {
1383                    if (isInSet(mVisibleTrackedIds, id)) {
1384                        mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1385                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1386                    }
1387                }
1388            }
1389
1390            if (mVisibleTrackedIds == null) {
1391                if (sVerbose) {
1392                    Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1393                }
1394                finishSessionLocked();
1395            }
1396        }
1397
1398        /**
1399         * Called once the client becomes visible.
1400         *
1401         * @see AutofillClient#isVisibleForAutofill()
1402         */
1403        void onVisibleForAutofillLocked() {
1404            // The visibility of the views might have changed while the client was not be visible,
1405            // hence update the visibility state for all views.
1406            AutofillClient client = getClientLocked();
1407            ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1408            ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1409            if (client != null) {
1410                if (mInvisibleTrackedIds != null) {
1411                    final ArrayList<AutofillId> orderedInvisibleIds =
1412                            new ArrayList<>(mInvisibleTrackedIds);
1413                    final boolean[] isVisible = client.getViewVisibility(
1414                            getViewIds(orderedInvisibleIds));
1415
1416                    final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1417                    for (int i = 0; i < numInvisibleTrackedIds; i++) {
1418                        final AutofillId id = orderedInvisibleIds.get(i);
1419                        if (isVisible[i]) {
1420                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1421
1422                            if (sDebug) {
1423                                Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
1424                            }
1425                        } else {
1426                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1427                        }
1428                    }
1429                }
1430
1431                if (mVisibleTrackedIds != null) {
1432                    final ArrayList<AutofillId> orderedVisibleIds =
1433                            new ArrayList<>(mVisibleTrackedIds);
1434                    final boolean[] isVisible = client.getViewVisibility(
1435                            getViewIds(orderedVisibleIds));
1436
1437                    final int numVisibleTrackedIds = orderedVisibleIds.size();
1438                    for (int i = 0; i < numVisibleTrackedIds; i++) {
1439                        final AutofillId id = orderedVisibleIds.get(i);
1440
1441                        if (isVisible[i]) {
1442                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1443                        } else {
1444                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1445
1446                            if (sDebug) {
1447                                Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
1448                            }
1449                        }
1450                    }
1451                }
1452
1453                mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1454                mVisibleTrackedIds = updatedVisibleTrackedIds;
1455            }
1456
1457            if (mVisibleTrackedIds == null) {
1458                finishSessionLocked();
1459            }
1460        }
1461    }
1462
1463    /**
1464     * Callback for auto-fill related events.
1465     *
1466     * <p>Typically used for applications that display their own "auto-complete" views, so they can
1467     * enable / disable such views when the auto-fill UI affordance is shown / hidden.
1468     */
1469    public abstract static class AutofillCallback {
1470
1471        /** @hide */
1472        @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1473        @Retention(RetentionPolicy.SOURCE)
1474        public @interface AutofillEventType {}
1475
1476        /**
1477         * The auto-fill input UI affordance associated with the view was shown.
1478         *
1479         * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1480         * should be hidden upon receiving this event.
1481         */
1482        public static final int EVENT_INPUT_SHOWN = 1;
1483
1484        /**
1485         * The auto-fill input UI affordance associated with the view was hidden.
1486         *
1487         * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1488         * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1489         */
1490        public static final int EVENT_INPUT_HIDDEN = 2;
1491
1492        /**
1493         * The auto-fill input UI affordance associated with the view won't be shown because
1494         * autofill is not available.
1495         *
1496         * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1497         * to avoid flickering, it could shown it upon receiving this event.
1498         */
1499        public static final int EVENT_INPUT_UNAVAILABLE = 3;
1500
1501        /**
1502         * Called after a change in the autofill state associated with a view.
1503         *
1504         * @param view view associated with the change.
1505         *
1506         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1507         */
1508        public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1509        }
1510
1511        /**
1512         * Called after a change in the autofill state associated with a virtual view.
1513         *
1514         * @param view parent view associated with the change.
1515         * @param virtualId id identifying the virtual child inside the parent view.
1516         *
1517         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1518         */
1519        public void onAutofillEvent(@NonNull View view, int virtualId,
1520                @AutofillEventType int event) {
1521        }
1522    }
1523
1524    private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1525        private final WeakReference<AutofillManager> mAfm;
1526
1527        AutofillManagerClient(AutofillManager autofillManager) {
1528            mAfm = new WeakReference<>(autofillManager);
1529        }
1530
1531        @Override
1532        public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
1533            final AutofillManager afm = mAfm.get();
1534            if (afm != null) {
1535                afm.post(() -> afm.setState(enabled, resetSession, resetClient));
1536            }
1537        }
1538
1539        @Override
1540        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
1541            final AutofillManager afm = mAfm.get();
1542            if (afm != null) {
1543                afm.post(() -> afm.autofill(sessionId, ids, values));
1544            }
1545        }
1546
1547        @Override
1548        public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1549                Intent fillInIntent) {
1550            final AutofillManager afm = mAfm.get();
1551            if (afm != null) {
1552                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
1553            }
1554        }
1555
1556        @Override
1557        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1558                Rect anchorBounds, IAutofillWindowPresenter presenter) {
1559            final AutofillManager afm = mAfm.get();
1560            if (afm != null) {
1561                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1562                        presenter));
1563            }
1564        }
1565
1566        @Override
1567        public void requestHideFillUi(int sessionId, AutofillId id) {
1568            final AutofillManager afm = mAfm.get();
1569            if (afm != null) {
1570                afm.post(() -> afm.requestHideFillUi(id));
1571            }
1572        }
1573
1574        @Override
1575        public void notifyNoFillUi(int sessionId, AutofillId id) {
1576            final AutofillManager afm = mAfm.get();
1577            if (afm != null) {
1578                afm.post(() -> afm.notifyNoFillUi(sessionId, id));
1579            }
1580        }
1581
1582        @Override
1583        public void startIntentSender(IntentSender intentSender) {
1584            final AutofillManager afm = mAfm.get();
1585            if (afm != null) {
1586                afm.post(() -> {
1587                    try {
1588                        afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
1589                    } catch (IntentSender.SendIntentException e) {
1590                        Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1591                    }
1592                });
1593            }
1594        }
1595
1596        @Override
1597        public void setTrackedViews(int sessionId, AutofillId[] ids,
1598                boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
1599            final AutofillManager afm = mAfm.get();
1600            if (afm != null) {
1601                afm.post(() ->
1602                        afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
1603                );
1604            }
1605        }
1606    }
1607}
1608