AutofillManager.java revision d46f87a9e2b444b6f9999d7ec86d347e2fe1b596
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.ComponentName;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentSender;
31import android.graphics.Rect;
32import android.metrics.LogMaker;
33import android.os.Bundle;
34import android.os.IBinder;
35import android.os.Parcelable;
36import android.os.RemoteException;
37import android.service.autofill.AutofillService;
38import android.service.autofill.FillEventHistory;
39import android.util.ArrayMap;
40import android.util.ArraySet;
41import android.util.Log;
42import android.util.SparseArray;
43import android.view.View;
44
45import com.android.internal.annotations.GuardedBy;
46import com.android.internal.logging.MetricsLogger;
47import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
48
49import java.io.PrintWriter;
50import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
52import java.lang.ref.WeakReference;
53import java.util.ArrayList;
54import java.util.List;
55import java.util.Objects;
56
57/**
58 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
59 * Autofill Framework lifecycle.
60 *
61 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
62 * activity context; the autofill context is created when one of the following methods is called for
63 * the first time in an activity context, and the current user has an enabled autofill service:
64 *
65 * <ul>
66 *   <li>{@link #notifyViewEntered(View)}
67 *   <li>{@link #notifyViewEntered(View, int, Rect)}
68 *   <li>{@link #requestAutofill(View)}
69 * </ul>
70 *
71 * <p>Tipically, the context is automatically created when the first view of the activity is
72 * focused because {@code View.onFocusChanged()} indirectly calls
73 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
74 * explicitly create it (for example, a custom view developer could offer a contextual menu action
75 * in a text-field view to let users manually request autofill).
76 *
77 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
78 * that represents the view hierarchy by calling
79 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
80 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
81 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
82 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
83 * the hierarchy.
84 *
85 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
86 * parses it looking for views that can be autofilled. If the service finds such views, it returns
87 * a data structure to the Android System containing the following optional info:
88 *
89 * <ul>
90 *   <li>Datasets used to autofill subsets of views in the activity.
91 *   <li>Id of views that the service can save their values for future autofilling.
92 * </ul>
93 *
94 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
95 * UI associated with the view, when the view is focused on and is part of a dataset.
96 * The application can be notified when the UI is shown by registering an
97 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
98 * selects a dataset from the UI, all views present in the dataset are autofilled, through
99 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
100 *
101 * <p>When the service returns ids of savable views, the Android System keeps track of changes
102 * made to these views, so they can be used to determine if the autofill save UI is shown later.
103 *
104 * <p>The context is then finished when one of the following occurs:
105 *
106 * <ul>
107 *   <li>{@link #commit()} is called or all savable views are gone.
108 *   <li>{@link #cancel()} is called.
109 * </ul>
110 *
111 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
112 * shows an autofill save UI if the value of savable views have changed. If the user selects the
113 * option to Save, the current value of the views is then sent to the autofill service.
114 *
115 * <p>It is safe to call into its methods from any thread.
116 */
117@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
118public final class AutofillManager {
119
120    private static final String TAG = "AutofillManager";
121
122    /**
123     * Intent extra: The assist structure which captures the filled screen.
124     *
125     * <p>
126     * Type: {@link android.app.assist.AssistStructure}
127     */
128    public static final String EXTRA_ASSIST_STRUCTURE =
129            "android.view.autofill.extra.ASSIST_STRUCTURE";
130
131    /**
132     * Intent extra: The result of an authentication operation. It is
133     * either a fully populated {@link android.service.autofill.FillResponse}
134     * or a fully populated {@link android.service.autofill.Dataset} if
135     * a response or a dataset is being authenticated respectively.
136     *
137     * <p>
138     * Type: {@link android.service.autofill.FillResponse} or a
139     * {@link android.service.autofill.Dataset}
140     */
141    public static final String EXTRA_AUTHENTICATION_RESULT =
142            "android.view.autofill.extra.AUTHENTICATION_RESULT";
143
144    /**
145     * Intent extra: The optional extras provided by the
146     * {@link android.service.autofill.AutofillService}.
147     *
148     * <p>For example, when the service responds to a {@link
149     * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
150     * a {@code FillResponse} that requires authentication, the Intent that launches the
151     * service authentication will contain the Bundle set by
152     * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
153     *
154     * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
155     * can also add this bundle to the {@link Intent} set as the
156     * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
157     * so the bundle can be recovered later on
158     * {@link android.service.autofill.SaveRequest#getClientState()}.
159     *
160     * <p>
161     * Type: {@link android.os.Bundle}
162     */
163    public static final String EXTRA_CLIENT_STATE =
164            "android.view.autofill.extra.CLIENT_STATE";
165
166
167    /** @hide */
168    public static final String EXTRA_RESTORE_SESSION_TOKEN =
169            "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
170
171    private static final String SESSION_ID_TAG = "android:sessionId";
172    private static final String STATE_TAG = "android:state";
173    private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
174
175
176    /** @hide */ public static final int ACTION_START_SESSION = 1;
177    /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
178    /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
179    /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
180
181
182    /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
183    /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
184    /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
185
186    /** Which bits in an authentication id are used for the dataset id */
187    private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
188    /** How many bits in an authentication id are used for the dataset id */
189    private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
190    /** @hide The index for an undefined data set */
191    public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
192
193    /**
194     * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
195     *
196     * @hide
197     */
198    public static final int PENDING_UI_OPERATION_CANCEL = 1;
199
200    /**
201     * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
202     *
203     * @hide
204     */
205    public static final int PENDING_UI_OPERATION_RESTORE = 2;
206
207    /**
208     * Initial state of the autofill context, set when there is no session (i.e., when
209     * {@link #mSessionId} is {@link #NO_SESSION}).
210     *
211     * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
212     * the server.
213     *
214     * @hide
215     */
216    public static final int STATE_UNKNOWN = 0;
217
218    /**
219     * State where the autofill context hasn't been {@link #commit() finished} nor
220     * {@link #cancel() canceled} yet.
221     *
222     * @hide
223     */
224    public static final int STATE_ACTIVE = 1;
225
226    /**
227     * State where the autofill context was finished by the server because the autofill
228     * service could not autofill the activity.
229     *
230     * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
231     * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
232     *
233     * @hide
234     */
235    public static final int STATE_FINISHED = 2;
236
237    /**
238     * State where the autofill context has been {@link #commit() finished} but the server still has
239     * a session because the Save UI hasn't been dismissed yet.
240     *
241     * @hide
242     */
243    public static final int STATE_SHOWING_SAVE_UI = 3;
244
245    /**
246     * State where the autofill is disabled because the service cannot autofill the activity at all.
247     *
248     * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
249     * (and {@link #requestAutofill(View, int, Rect)}).
250     *
251     * @hide
252     */
253    public static final int STATE_DISABLED_BY_SERVICE = 4;
254
255    /**
256     * Makes an authentication id from a request id and a dataset id.
257     *
258     * @param requestId The request id.
259     * @param datasetId The dataset id.
260     * @return The authentication id.
261     * @hide
262     */
263    public static int makeAuthenticationId(int requestId, int datasetId) {
264        return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
265                | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
266    }
267
268    /**
269     * Gets the request id from an authentication id.
270     *
271     * @param authRequestId The authentication id.
272     * @return The request id.
273     * @hide
274     */
275    public static int getRequestIdFromAuthenticationId(int authRequestId) {
276        return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
277    }
278
279    /**
280     * Gets the dataset id from an authentication id.
281     *
282     * @param authRequestId The authentication id.
283     * @return The dataset id.
284     * @hide
285     */
286    public static int getDatasetIdFromAuthenticationId(int authRequestId) {
287        return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
288    }
289
290    private final MetricsLogger mMetricsLogger = new MetricsLogger();
291
292    /**
293     * There is currently no session running.
294     * {@hide}
295     */
296    public static final int NO_SESSION = Integer.MIN_VALUE;
297
298    private final IAutoFillManager mService;
299
300    private final Object mLock = new Object();
301
302    @GuardedBy("mLock")
303    private IAutoFillManagerClient mServiceClient;
304
305    @GuardedBy("mLock")
306    private AutofillCallback mCallback;
307
308    private final Context mContext;
309
310    @GuardedBy("mLock")
311    private int mSessionId = NO_SESSION;
312
313    @GuardedBy("mLock")
314    private int mState = STATE_UNKNOWN;
315
316    @GuardedBy("mLock")
317    private boolean mEnabled;
318
319    /** If a view changes to this mapping the autofill operation was successful */
320    @GuardedBy("mLock")
321    @Nullable private ParcelableMap mLastAutofilledData;
322
323    /** If view tracking is enabled, contains the tracking state */
324    @GuardedBy("mLock")
325    @Nullable private TrackedViews mTrackedViews;
326
327    /** Views that are only tracked because they are fillable and could be anchoring the UI. */
328    @GuardedBy("mLock")
329    @Nullable private ArraySet<AutofillId> mFillableIds;
330
331    /** If set, session is commited when the field is clicked. */
332    @GuardedBy("mLock")
333    @Nullable private AutofillId mSaveTriggerId;
334
335    /** If set, session is commited when the activity is finished; otherwise session is canceled. */
336    @GuardedBy("mLock")
337    private boolean mSaveOnFinish;
338
339    /** @hide */
340    public interface AutofillClient {
341        /**
342         * Asks the client to start an authentication flow.
343         *
344         * @param authenticationId A unique id of the authentication operation.
345         * @param intent The authentication intent.
346         * @param fillInIntent The authentication fill-in intent.
347         */
348        void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
349                Intent fillInIntent);
350
351        /**
352         * Tells the client this manager has state to be reset.
353         */
354        void autofillCallbackResetableStateAvailable();
355
356        /**
357         * Request showing the autofill UI.
358         *
359         * @param anchor The real view the UI needs to anchor to.
360         * @param width The width of the fill UI content.
361         * @param height The height of the fill UI content.
362         * @param virtualBounds The bounds of the virtual decendant of the anchor.
363         * @param presenter The presenter that controls the fill UI window.
364         * @return Whether the UI was shown.
365         */
366        boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
367                @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
368
369        /**
370         * Request hiding the autofill UI.
371         *
372         * @return Whether the UI was hidden.
373         */
374        boolean autofillCallbackRequestHideFillUi();
375
376        /**
377         * Checks if views are currently attached and visible.
378         *
379         * @return And array with {@code true} iff the view is attached or visible
380         */
381        @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
382
383        /**
384         * Checks is the client is currently visible as understood by autofill.
385         *
386         * @return {@code true} if the client is currently visible
387         */
388        boolean isVisibleForAutofill();
389
390        /**
391         * Finds views by traversing the hierarchies of the client.
392         *
393         * @param viewIds The autofill ids of the views to find
394         *
395         * @return And array containing the views (empty if no views found).
396         */
397        @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
398
399        /**
400         * Finds a view by traversing the hierarchies of the client.
401         *
402         * @param viewId The autofill id of the views to find
403         *
404         * @return The view, or {@code null} if not found
405         */
406        @Nullable View findViewByAutofillIdTraversal(int viewId);
407
408        /**
409         * Runs the specified action on the UI thread.
410         */
411        void runOnUiThread(Runnable action);
412
413        /**
414         * Gets the complete component name of this client.
415         */
416        ComponentName getComponentName();
417    }
418
419    /**
420     * @hide
421     */
422    public AutofillManager(Context context, IAutoFillManager service) {
423        mContext = context;
424        mService = service;
425    }
426
427    /**
428     * Restore state after activity lifecycle
429     *
430     * @param savedInstanceState The state to be restored
431     *
432     * {@hide}
433     */
434    public void onCreate(Bundle savedInstanceState) {
435        if (!hasAutofillFeature()) {
436            return;
437        }
438        synchronized (mLock) {
439            mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
440
441            if (isActiveLocked()) {
442                Log.w(TAG, "New session was started before onCreate()");
443                return;
444            }
445
446            mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
447            mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
448
449            if (mSessionId != NO_SESSION) {
450                ensureServiceClientAddedIfNeededLocked();
451
452                final AutofillClient client = getClientLocked();
453                if (client != null) {
454                    try {
455                        final boolean sessionWasRestored = mService.restoreSession(mSessionId,
456                                mContext.getActivityToken(), mServiceClient.asBinder());
457
458                        if (!sessionWasRestored) {
459                            Log.w(TAG, "Session " + mSessionId + " could not be restored");
460                            mSessionId = NO_SESSION;
461                            mState = STATE_UNKNOWN;
462                        } else {
463                            if (sDebug) {
464                                Log.d(TAG, "session " + mSessionId + " was restored");
465                            }
466
467                            client.autofillCallbackResetableStateAvailable();
468                        }
469                    } catch (RemoteException e) {
470                        Log.e(TAG, "Could not figure out if there was an autofill session", e);
471                    }
472                }
473            }
474        }
475    }
476
477    /**
478     * Called once the client becomes visible.
479     *
480     * @see AutofillClient#isVisibleForAutofill()
481     *
482     * {@hide}
483     */
484    public void onVisibleForAutofill() {
485        synchronized (mLock) {
486            if (mEnabled && isActiveLocked() && mTrackedViews != null) {
487                mTrackedViews.onVisibleForAutofillLocked();
488            }
489        }
490    }
491
492    /**
493     * Save state before activity lifecycle
494     *
495     * @param outState Place to store the state
496     *
497     * {@hide}
498     */
499    public void onSaveInstanceState(Bundle outState) {
500        if (!hasAutofillFeature()) {
501            return;
502        }
503        synchronized (mLock) {
504            if (mSessionId != NO_SESSION) {
505                outState.putInt(SESSION_ID_TAG, mSessionId);
506            }
507            if (mState != STATE_UNKNOWN) {
508                outState.putInt(STATE_TAG, mState);
509            }
510            if (mLastAutofilledData != null) {
511                outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
512            }
513        }
514    }
515
516    /**
517     * Checks whether autofill is enabled for the current user.
518     *
519     * <p>Typically used to determine whether the option to explicitly request autofill should
520     * be offered - see {@link #requestAutofill(View)}.
521     *
522     * @return whether autofill is enabled for the current user.
523     */
524    public boolean isEnabled() {
525        if (!hasAutofillFeature() || isDisabledByService()) {
526            return false;
527        }
528        synchronized (mLock) {
529            ensureServiceClientAddedIfNeededLocked();
530            return mEnabled;
531        }
532    }
533
534    /**
535     * Should always be called from {@link AutofillService#getFillEventHistory()}.
536     *
537     * @hide
538     */
539    @Nullable public FillEventHistory getFillEventHistory() {
540        try {
541            return mService.getFillEventHistory();
542        } catch (RemoteException e) {
543            e.rethrowFromSystemServer();
544            return null;
545        }
546    }
547
548    /**
549     * Explicitly requests a new autofill context.
550     *
551     * <p>Normally, the autofill context is automatically started if necessary when
552     * {@link #notifyViewEntered(View)} is called, but this method should be used in the
553     * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
554     * option on its contextual overflow menu, and the user selects it.
555     *
556     * @param view view requesting the new autofill context.
557     */
558    public void requestAutofill(@NonNull View view) {
559        notifyViewEntered(view, FLAG_MANUAL_REQUEST);
560    }
561
562    /**
563     * Explicitly requests a new autofill context for virtual views.
564     *
565     * <p>Normally, the autofill context is automatically started if necessary when
566     * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
567     * cases where it must be explicitly started. For example, when the virtual view offers an
568     * AUTOFILL option on its contextual overflow menu, and the user selects it.
569     *
570     * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
571     * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
572     * the absolute bounds could be calculated by:
573     *
574     * <pre class="prettyprint">
575     *   int offset[] = new int[2];
576     *   getLocationOnScreen(offset);
577     *   Rect absBounds = new Rect(bounds.left + offset[0],
578     *       bounds.top + offset[1],
579     *       bounds.right + offset[0], bounds.bottom + offset[1]);
580     * </pre>
581     *
582     * @param view the virtual view parent.
583     * @param virtualId id identifying the virtual child inside the parent view.
584     * @param absBounds absolute boundaries of the virtual view in the screen.
585     */
586    public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
587        notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
588    }
589
590    /**
591     * Called when a {@link View} that supports autofill is entered.
592     *
593     * @param view {@link View} that was entered.
594     */
595    public void notifyViewEntered(@NonNull View view) {
596        notifyViewEntered(view, 0);
597    }
598
599    private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
600        if (isDisabledByService()) {
601            if (sVerbose) {
602                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
603                        + ") on state " + getStateAsStringLocked());
604            }
605            return true;
606        }
607        if (mState == STATE_FINISHED && (flags & FLAG_MANUAL_REQUEST) == 0) {
608            if (sVerbose) {
609                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
610                        + ") on state " + getStateAsStringLocked());
611            }
612            return true;
613        }
614        return false;
615    }
616
617    private void notifyViewEntered(@NonNull View view, int flags) {
618        if (!hasAutofillFeature()) {
619            return;
620        }
621        AutofillCallback callback = null;
622        synchronized (mLock) {
623            if (shouldIgnoreViewEnteredLocked(view, flags)) return;
624
625            ensureServiceClientAddedIfNeededLocked();
626
627            if (!mEnabled) {
628                if (mCallback != null) {
629                    callback = mCallback;
630                }
631            } else {
632                final AutofillId id = getAutofillId(view);
633                final AutofillValue value = view.getAutofillValue();
634
635                if (!isActiveLocked()) {
636                    // Starts new session.
637                    startSessionLocked(id, null, value, flags);
638                } else {
639                    // Update focus on existing session.
640                    updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
641                }
642            }
643        }
644
645        if (callback != null) {
646            mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
647        }
648    }
649
650    /**
651     * Called when a {@link View} that supports autofill is exited.
652     *
653     * @param view {@link View} that was exited.
654     */
655    public void notifyViewExited(@NonNull View view) {
656        if (!hasAutofillFeature()) {
657            return;
658        }
659        synchronized (mLock) {
660            ensureServiceClientAddedIfNeededLocked();
661
662            if (mEnabled && isActiveLocked()) {
663                final AutofillId id = getAutofillId(view);
664
665                // Update focus on existing session.
666                updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
667            }
668        }
669    }
670
671    /**
672     * Called when a {@link View view's} visibility changed.
673     *
674     * @param view {@link View} that was exited.
675     * @param isVisible visible if the view is visible in the view hierarchy.
676     */
677    public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
678        notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
679    }
680
681    /**
682     * Called when a virtual view's visibility changed.
683     *
684     * @param view {@link View} that was exited.
685     * @param virtualId id identifying the virtual child inside the parent view.
686     * @param isVisible visible if the view is visible in the view hierarchy.
687     */
688    public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
689        notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
690    }
691
692    /**
693     * Called when a view/virtual view's visibility changed.
694     *
695     * @param view {@link View} that was exited.
696     * @param virtualId id identifying the virtual child inside the parent view.
697     * @param isVisible visible if the view is visible in the view hierarchy.
698     * @param virtual Whether the view is virtual.
699     */
700    private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
701            boolean isVisible, boolean virtual) {
702        synchronized (mLock) {
703            if (mEnabled && isActiveLocked()) {
704                final AutofillId id = virtual ? getAutofillId(view, virtualId)
705                        : view.getAutofillId();
706                if (!isVisible && mFillableIds != null) {
707                    if (mFillableIds.contains(id)) {
708                        if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
709                        requestHideFillUi(id, view);
710                    }
711                }
712                if (mTrackedViews != null) {
713                    mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
714                }
715            }
716        }
717    }
718
719    /**
720     * Called when a virtual view that supports autofill is entered.
721     *
722     * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
723     * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
724     * the absolute bounds could be calculated by:
725     *
726     * <pre class="prettyprint">
727     *   int offset[] = new int[2];
728     *   getLocationOnScreen(offset);
729     *   Rect absBounds = new Rect(bounds.left + offset[0],
730     *       bounds.top + offset[1],
731     *       bounds.right + offset[0], bounds.bottom + offset[1]);
732     * </pre>
733     *
734     * @param view the virtual view parent.
735     * @param virtualId id identifying the virtual child inside the parent view.
736     * @param absBounds absolute boundaries of the virtual view in the screen.
737     */
738    public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
739        notifyViewEntered(view, virtualId, absBounds, 0);
740    }
741
742    private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
743        if (!hasAutofillFeature()) {
744            return;
745        }
746        AutofillCallback callback = null;
747        synchronized (mLock) {
748            if (shouldIgnoreViewEnteredLocked(view, flags)) return;
749
750            ensureServiceClientAddedIfNeededLocked();
751
752            if (!mEnabled) {
753                if (mCallback != null) {
754                    callback = mCallback;
755                }
756            } else {
757                final AutofillId id = getAutofillId(view, virtualId);
758
759                if (!isActiveLocked()) {
760                    // Starts new session.
761                    startSessionLocked(id, bounds, null, flags);
762                } else {
763                    // Update focus on existing session.
764                    updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
765                }
766            }
767        }
768
769        if (callback != null) {
770            callback.onAutofillEvent(view, virtualId,
771                    AutofillCallback.EVENT_INPUT_UNAVAILABLE);
772        }
773    }
774
775    /**
776     * Called when a virtual view that supports autofill is exited.
777     *
778     * @param view the virtual view parent.
779     * @param virtualId id identifying the virtual child inside the parent view.
780     */
781    public void notifyViewExited(@NonNull View view, int virtualId) {
782        if (!hasAutofillFeature()) {
783            return;
784        }
785        synchronized (mLock) {
786            ensureServiceClientAddedIfNeededLocked();
787
788            if (mEnabled && isActiveLocked()) {
789                final AutofillId id = getAutofillId(view, virtualId);
790
791                // Update focus on existing session.
792                updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
793            }
794        }
795    }
796
797    /**
798     * Called to indicate the value of an autofillable {@link View} changed.
799     *
800     * @param view view whose value changed.
801     */
802    public void notifyValueChanged(View view) {
803        if (!hasAutofillFeature()) {
804            return;
805        }
806        AutofillId id = null;
807        boolean valueWasRead = false;
808        AutofillValue value = null;
809
810        synchronized (mLock) {
811            // If the session is gone some fields might still be highlighted, hence we have to
812            // remove the isAutofilled property even if no sessions are active.
813            if (mLastAutofilledData == null) {
814                view.setAutofilled(false);
815            } else {
816                id = getAutofillId(view);
817                if (mLastAutofilledData.containsKey(id)) {
818                    value = view.getAutofillValue();
819                    valueWasRead = true;
820
821                    if (Objects.equals(mLastAutofilledData.get(id), value)) {
822                        view.setAutofilled(true);
823                    } else {
824                        view.setAutofilled(false);
825                        mLastAutofilledData.remove(id);
826                    }
827                } else {
828                    view.setAutofilled(false);
829                }
830            }
831
832            if (!mEnabled || !isActiveLocked()) {
833                if (sVerbose && mEnabled) {
834                    Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
835                            + getStateAsStringLocked());
836                }
837                return;
838            }
839
840            if (id == null) {
841                id = getAutofillId(view);
842            }
843
844            if (!valueWasRead) {
845                value = view.getAutofillValue();
846            }
847
848            updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
849        }
850    }
851
852    /**
853     * Called to indicate the value of an autofillable virtual view has changed.
854     *
855     * @param view the virtual view parent.
856     * @param virtualId id identifying the virtual child inside the parent view.
857     * @param value new value of the child.
858     */
859    public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
860        if (!hasAutofillFeature()) {
861            return;
862        }
863        synchronized (mLock) {
864            if (!mEnabled || !isActiveLocked()) {
865                return;
866            }
867
868            final AutofillId id = getAutofillId(view, virtualId);
869            updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
870        }
871    }
872
873
874    /**
875     * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
876     *
877     * @hide
878     */
879    public void notifyViewClicked(View view) {
880        final AutofillId id = view.getAutofillId();
881
882        if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
883
884        synchronized (mLock) {
885            if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
886                if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
887                commitLocked();
888                mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
889                        mContext.getPackageName());
890            }
891        }
892    }
893
894    /**
895     * Called by {@link android.app.Activity} to commit or cancel the session on finish.
896     *
897     * @hide
898     */
899    public void onActivityFinished() {
900        if (!hasAutofillFeature()) {
901            return;
902        }
903        synchronized (mLock) {
904            if (mSaveOnFinish) {
905                commitLocked();
906            } else {
907                if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
908                cancelLocked();
909            }
910        }
911    }
912
913    /**
914     * Called to indicate the current autofill context should be commited.
915     *
916     * <p>This method is typically called by {@link View Views} that manage virtual views; for
917     * example, when the view is rendering an {@code HTML} page with a form and virtual views
918     * that represent the HTML elements, it should call this method after the form is submitted and
919     * another page is rendered.
920     *
921     * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
922     * methods such as {@link android.app.Activity#finish()}.
923     */
924    public void commit() {
925        if (!hasAutofillFeature()) {
926            return;
927        }
928        synchronized (mLock) {
929            commitLocked();
930        }
931    }
932
933    private void commitLocked() {
934        if (!mEnabled && !isActiveLocked()) {
935            return;
936        }
937        finishSessionLocked();
938    }
939
940    /**
941     * Called to indicate the current autofill context should be cancelled.
942     *
943     * <p>This method is typically called by {@link View Views} that manage virtual views; for
944     * example, when the view is rendering an {@code HTML} page with a form and virtual views
945     * that represent the HTML elements, it should call this method if the user does not post the
946     * form but moves to another form in this page.
947     *
948     * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
949     * methods such as {@link android.app.Activity#finish()}.
950     */
951    public void cancel() {
952        if (!hasAutofillFeature()) {
953            return;
954        }
955        synchronized (mLock) {
956            cancelLocked();
957        }
958    }
959
960    private void cancelLocked() {
961        if (!mEnabled && !isActiveLocked()) {
962            return;
963        }
964        cancelSessionLocked();
965    }
966
967    /** @hide */
968    public void disableOwnedAutofillServices() {
969        disableAutofillServices();
970    }
971
972    /**
973     * If the app calling this API has enabled autofill services they
974     * will be disabled.
975     */
976    public void disableAutofillServices() {
977        if (!hasAutofillFeature()) {
978            return;
979        }
980        try {
981            mService.disableOwnedAutofillServices(mContext.getUserId());
982        } catch (RemoteException e) {
983            throw e.rethrowFromSystemServer();
984        }
985    }
986
987    /**
988     * Returns {@code true} if the calling application provides a {@link AutofillService} that is
989     * enabled for the current user, or {@code false} otherwise.
990     */
991    public boolean hasEnabledAutofillServices() {
992        if (mService == null) return false;
993
994        try {
995            return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
996        } catch (RemoteException e) {
997            throw e.rethrowFromSystemServer();
998        }
999    }
1000
1001    /**
1002     * Returns {@code true} if autofill is supported by the current device and
1003     * is supported for this user.
1004     *
1005     * <p>Autofill is typically supported, but it could be unsupported in cases like:
1006     * <ol>
1007     *     <li>Low-end devices.
1008     *     <li>Device policy rules that forbid its usage.
1009     * </ol>
1010     */
1011    public boolean isAutofillSupported() {
1012        if (mService == null) return false;
1013
1014        try {
1015            return mService.isServiceSupported(mContext.getUserId());
1016        } catch (RemoteException e) {
1017            throw e.rethrowFromSystemServer();
1018        }
1019    }
1020
1021    private AutofillClient getClientLocked() {
1022        final AutofillClient client = mContext.getAutofillClient();
1023        if (client == null && sDebug) {
1024            Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1025                    + mContext);
1026        }
1027        return client;
1028    }
1029
1030    /** @hide */
1031    public void onAuthenticationResult(int authenticationId, Intent data) {
1032        if (!hasAutofillFeature()) {
1033            return;
1034        }
1035        // TODO: the result code is being ignored, so this method is not reliably
1036        // handling the cases where it's not RESULT_OK: it works fine if the service does not
1037        // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1038        // service set the extra and returned RESULT_CANCELED...
1039
1040        if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
1041
1042        synchronized (mLock) {
1043            if (!isActiveLocked() || data == null) {
1044                return;
1045            }
1046            final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1047            final Bundle responseData = new Bundle();
1048            responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
1049            final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1050            if (newClientState != null) {
1051                responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1052            }
1053            try {
1054                mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1055                        mContext.getUserId());
1056            } catch (RemoteException e) {
1057                Log.e(TAG, "Error delivering authentication result", e);
1058            }
1059        }
1060    }
1061
1062    private static AutofillId getAutofillId(View view) {
1063        return new AutofillId(view.getAutofillViewId());
1064    }
1065
1066    private static AutofillId getAutofillId(View parent, int virtualId) {
1067        return new AutofillId(parent.getAutofillViewId(), virtualId);
1068    }
1069
1070    private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1071            @NonNull AutofillValue value, int flags) {
1072        if (sVerbose) {
1073            Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
1074                    + ", flags=" + flags + ", state=" + getStateAsStringLocked());
1075        }
1076        if (mState != STATE_UNKNOWN && (flags & FLAG_MANUAL_REQUEST) == 0) {
1077            if (sVerbose) {
1078                Log.v(TAG, "not automatically starting session for " + id
1079                        + " on state " + getStateAsStringLocked());
1080            }
1081            return;
1082        }
1083        try {
1084            final AutofillClient client = getClientLocked();
1085            mSessionId = mService.startSession(mContext.getActivityToken(),
1086                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1087                    mCallback != null, flags, client.getComponentName());
1088            if (mSessionId != NO_SESSION) {
1089                mState = STATE_ACTIVE;
1090            }
1091            if (client != null) {
1092                client.autofillCallbackResetableStateAvailable();
1093            }
1094        } catch (RemoteException e) {
1095            throw e.rethrowFromSystemServer();
1096        }
1097    }
1098
1099    private void finishSessionLocked() {
1100        if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
1101
1102        if (!isActiveLocked()) return;
1103
1104        try {
1105            mService.finishSession(mSessionId, mContext.getUserId());
1106        } catch (RemoteException e) {
1107            throw e.rethrowFromSystemServer();
1108        }
1109
1110        resetSessionLocked();
1111    }
1112
1113    private void cancelSessionLocked() {
1114        if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
1115
1116        if (!isActiveLocked()) return;
1117
1118        try {
1119            mService.cancelSession(mSessionId, mContext.getUserId());
1120        } catch (RemoteException e) {
1121            throw e.rethrowFromSystemServer();
1122        }
1123
1124        resetSessionLocked();
1125    }
1126
1127    private void resetSessionLocked() {
1128        mSessionId = NO_SESSION;
1129        mState = STATE_UNKNOWN;
1130        mTrackedViews = null;
1131        mFillableIds = null;
1132        mSaveTriggerId = null;
1133    }
1134
1135    private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1136            int flags) {
1137        if (sVerbose && action != ACTION_VIEW_EXITED) {
1138            Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1139                    + ", value=" + value + ", action=" + action + ", flags=" + flags);
1140        }
1141        boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1142
1143        try {
1144            if (restartIfNecessary) {
1145                final AutofillClient client = getClientLocked();
1146                final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1147                        mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1148                        mCallback != null, flags, client.getComponentName(), mSessionId, action);
1149                if (newId != mSessionId) {
1150                    if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1151                    mSessionId = newId;
1152                    mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
1153                    if (client != null) {
1154                        client.autofillCallbackResetableStateAvailable();
1155                    }
1156                }
1157            } else {
1158                mService.updateSession(mSessionId, id, bounds, value, action, flags,
1159                        mContext.getUserId());
1160            }
1161
1162        } catch (RemoteException e) {
1163            throw e.rethrowFromSystemServer();
1164        }
1165    }
1166
1167    private void ensureServiceClientAddedIfNeededLocked() {
1168        if (getClientLocked() == null) {
1169            return;
1170        }
1171
1172        if (mServiceClient == null) {
1173            mServiceClient = new AutofillManagerClient(this);
1174            try {
1175                final int flags = mService.addClient(mServiceClient, mContext.getUserId());
1176                mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1177                sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1178                sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
1179            } catch (RemoteException e) {
1180                throw e.rethrowFromSystemServer();
1181            }
1182        }
1183    }
1184
1185    /**
1186     * Registers a {@link AutofillCallback} to receive autofill events.
1187     *
1188     * @param callback callback to receive events.
1189     */
1190    public void registerCallback(@Nullable AutofillCallback callback) {
1191        if (!hasAutofillFeature()) {
1192            return;
1193        }
1194        synchronized (mLock) {
1195            if (callback == null) return;
1196
1197            final boolean hadCallback = mCallback != null;
1198            mCallback = callback;
1199
1200            if (!hadCallback) {
1201                try {
1202                    mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1203                } catch (RemoteException e) {
1204                    throw e.rethrowFromSystemServer();
1205                }
1206            }
1207        }
1208    }
1209
1210    /**
1211     * Unregisters a {@link AutofillCallback} to receive autofill events.
1212     *
1213     * @param callback callback to stop receiving events.
1214     */
1215    public void unregisterCallback(@Nullable AutofillCallback callback) {
1216        if (!hasAutofillFeature()) {
1217            return;
1218        }
1219        synchronized (mLock) {
1220            if (callback == null || mCallback == null || callback != mCallback) return;
1221
1222            mCallback = null;
1223
1224            try {
1225                mService.setHasCallback(mSessionId, mContext.getUserId(), false);
1226            } catch (RemoteException e) {
1227                throw e.rethrowFromSystemServer();
1228            }
1229        }
1230    }
1231
1232    private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1233            Rect anchorBounds, IAutofillWindowPresenter presenter) {
1234        final View anchor = findView(id);
1235        if (anchor == null) {
1236            return;
1237        }
1238
1239        AutofillCallback callback = null;
1240        synchronized (mLock) {
1241            if (mSessionId == sessionId) {
1242                AutofillClient client = getClientLocked();
1243
1244                if (client != null) {
1245                    if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1246                            anchorBounds, presenter) && mCallback != null) {
1247                        callback = mCallback;
1248                    }
1249                }
1250            }
1251        }
1252
1253        if (callback != null) {
1254            if (id.isVirtual()) {
1255                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1256                        AutofillCallback.EVENT_INPUT_SHOWN);
1257            } else {
1258                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
1259            }
1260        }
1261    }
1262
1263    private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1264            Intent fillInIntent) {
1265        synchronized (mLock) {
1266            if (sessionId == mSessionId) {
1267                AutofillClient client = getClientLocked();
1268                if (client != null) {
1269                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
1270                }
1271            }
1272        }
1273    }
1274
1275    /** @hide */
1276    public static final int SET_STATE_FLAG_ENABLED = 0x01;
1277    /** @hide */
1278    public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1279    /** @hide */
1280    public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1281    /** @hide */
1282    public static final int SET_STATE_FLAG_DEBUG = 0x08;
1283    /** @hide */
1284    public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1285
1286    private void setState(int flags) {
1287        if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
1288        synchronized (mLock) {
1289            mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1290            if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
1291                // Reset the session state
1292                resetSessionLocked();
1293            }
1294            if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
1295                // Reset connection to system
1296                mServiceClient = null;
1297            }
1298        }
1299        sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1300        sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
1301    }
1302
1303    /**
1304     * Sets a view as autofilled if the current value is the {code targetValue}.
1305     *
1306     * @param view The view that is to be autofilled
1307     * @param targetValue The value we want to fill into view
1308     */
1309    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1310        AutofillValue currentValue = view.getAutofillValue();
1311        if (Objects.equals(currentValue, targetValue)) {
1312            synchronized (mLock) {
1313                if (mLastAutofilledData == null) {
1314                    mLastAutofilledData = new ParcelableMap(1);
1315                }
1316                mLastAutofilledData.put(getAutofillId(view), targetValue);
1317            }
1318            view.setAutofilled(true);
1319        }
1320    }
1321
1322    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
1323        synchronized (mLock) {
1324            if (sessionId != mSessionId) {
1325                return;
1326            }
1327
1328            final AutofillClient client = getClientLocked();
1329            if (client == null) {
1330                return;
1331            }
1332
1333            final int itemCount = ids.size();
1334            int numApplied = 0;
1335            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
1336            final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
1337
1338            for (int i = 0; i < itemCount; i++) {
1339                final AutofillId id = ids.get(i);
1340                final AutofillValue value = values.get(i);
1341                final int viewId = id.getViewId();
1342                final View view = views[i];
1343                if (view == null) {
1344                    Log.w(TAG, "autofill(): no View with id " + viewId);
1345                    continue;
1346                }
1347                if (id.isVirtual()) {
1348                    if (virtualValues == null) {
1349                        // Most likely there will be just one view with virtual children.
1350                        virtualValues = new ArrayMap<>(1);
1351                    }
1352                    SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1353                    if (valuesByParent == null) {
1354                        // We don't know the size yet, but usually it will be just a few fields...
1355                        valuesByParent = new SparseArray<>(5);
1356                        virtualValues.put(view, valuesByParent);
1357                    }
1358                    valuesByParent.put(id.getVirtualChildId(), value);
1359                } else {
1360                    // Mark the view as to be autofilled with 'value'
1361                    if (mLastAutofilledData == null) {
1362                        mLastAutofilledData = new ParcelableMap(itemCount - i);
1363                    }
1364                    mLastAutofilledData.put(id, value);
1365
1366                    view.autofill(value);
1367
1368                    // Set as autofilled if the values match now, e.g. when the value was updated
1369                    // synchronously.
1370                    // If autofill happens async, the view is set to autofilled in
1371                    // notifyValueChanged.
1372                    setAutofilledIfValuesIs(view, value);
1373
1374                    numApplied++;
1375                }
1376            }
1377
1378            if (virtualValues != null) {
1379                for (int i = 0; i < virtualValues.size(); i++) {
1380                    final View parent = virtualValues.keyAt(i);
1381                    final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1382                    parent.autofill(childrenValues);
1383                    numApplied += childrenValues.size();
1384                }
1385            }
1386
1387            final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1388                    .setPackageName(mContext.getPackageName())
1389                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1390                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
1391            mMetricsLogger.write(log);
1392        }
1393    }
1394
1395    /**
1396     *  Set the tracked views.
1397     *
1398     * @param trackedIds The views to be tracked.
1399     * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
1400     * @param saveOnFinish Finish the session once the activity is finished.
1401     * @param fillableIds Views that might anchor FillUI.
1402     * @param saveTriggerId View that when clicked triggers commit().
1403     */
1404    private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1405            boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1406            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
1407        synchronized (mLock) {
1408            if (mEnabled && mSessionId == sessionId) {
1409                if (saveOnAllViewsInvisible) {
1410                    mTrackedViews = new TrackedViews(trackedIds);
1411                } else {
1412                    mTrackedViews = null;
1413                }
1414                mSaveOnFinish = saveOnFinish;
1415                if (fillableIds != null) {
1416                    if (mFillableIds == null) {
1417                        mFillableIds = new ArraySet<>(fillableIds.length);
1418                    }
1419                    for (AutofillId id : fillableIds) {
1420                        mFillableIds.add(id);
1421                    }
1422                    if (sVerbose) {
1423                        Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1424                                + ", mFillableIds" + mFillableIds);
1425                    }
1426                }
1427
1428                if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1429                    // Turn off trigger on previous view id.
1430                    setNotifyOnClickLocked(mSaveTriggerId, false);
1431                }
1432
1433                if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1434                    // Turn on trigger on new view id.
1435                    mSaveTriggerId = saveTriggerId;
1436                    setNotifyOnClickLocked(mSaveTriggerId, true);
1437                }
1438            }
1439        }
1440    }
1441
1442    private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1443        final View view = findView(id);
1444        if (view == null) {
1445            Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1446            return;
1447        }
1448        view.setNotifyAutofillManagerOnClick(notify);
1449    }
1450
1451    private void setSaveUiState(int sessionId, boolean shown) {
1452        if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1453        synchronized (mLock) {
1454            if (mSessionId != NO_SESSION) {
1455                // Race condition: app triggered a new session after the previous session was
1456                // finished but before server called setSaveUiState() - need to cancel the new
1457                // session to avoid further inconsistent behavior.
1458                Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1459                        + ") called on existing session " + mSessionId + "; cancelling it");
1460                cancelSessionLocked();
1461            }
1462            if (shown) {
1463                mSessionId = sessionId;
1464                mState = STATE_SHOWING_SAVE_UI;
1465            } else {
1466                mSessionId = NO_SESSION;
1467                mState = STATE_UNKNOWN;
1468            }
1469        }
1470    }
1471
1472    /**
1473     * Marks the state of the session as finished.
1474     *
1475     * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
1476     *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1477     *  {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1478     *  requests for the activity).
1479     */
1480    private void setSessionFinished(int newState) {
1481        synchronized (mLock) {
1482            if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
1483            resetSessionLocked();
1484            mState = newState;
1485        }
1486    }
1487
1488    private void requestHideFillUi(AutofillId id) {
1489        final View anchor = findView(id);
1490        if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
1491        if (anchor == null) {
1492            return;
1493        }
1494        requestHideFillUi(id, anchor);
1495    }
1496
1497    private void requestHideFillUi(AutofillId id, View anchor) {
1498
1499        AutofillCallback callback = null;
1500        synchronized (mLock) {
1501            // We do not check the session id for two reasons:
1502            // 1. If local and remote session id are off sync the UI would be stuck shown
1503            // 2. There is a race between the user state being destroyed due the fill
1504            //    service being uninstalled and the UI being dismissed.
1505            AutofillClient client = getClientLocked();
1506            if (client != null) {
1507                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1508                    callback = mCallback;
1509                }
1510            }
1511        }
1512
1513        if (callback != null) {
1514            if (id.isVirtual()) {
1515                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1516                        AutofillCallback.EVENT_INPUT_HIDDEN);
1517            } else {
1518                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
1519            }
1520        }
1521    }
1522
1523    private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
1524        if (sVerbose) {
1525            Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
1526                    + ", sessionFinishedState=" + sessionFinishedState);
1527        }
1528        final View anchor = findView(id);
1529        if (anchor == null) {
1530            return;
1531        }
1532
1533        AutofillCallback callback = null;
1534        synchronized (mLock) {
1535            if (mSessionId == sessionId && getClientLocked() != null) {
1536                callback = mCallback;
1537            }
1538        }
1539
1540        if (callback != null) {
1541            if (id.isVirtual()) {
1542                callback.onAutofillEvent(anchor, id.getVirtualChildId(),
1543                        AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1544            } else {
1545                callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1546            }
1547        }
1548
1549        if (sessionFinishedState != 0) {
1550            // Callback call was "hijacked" to also update the session state.
1551            setSessionFinished(sessionFinishedState);
1552        }
1553    }
1554
1555    /**
1556     * Get an array of viewIds from a List of {@link AutofillId}.
1557     *
1558     * @param autofillIds The autofill ids to convert
1559     *
1560     * @return The array of viewIds.
1561     */
1562    // TODO: move to Helper as static method
1563    @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1564        final int numIds = autofillIds.length;
1565        final int[] viewIds = new int[numIds];
1566        for (int i = 0; i < numIds; i++) {
1567            viewIds[i] = autofillIds[i].getViewId();
1568        }
1569
1570        return viewIds;
1571    }
1572
1573    // TODO: move to Helper as static method
1574    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1575        final int numIds = autofillIds.size();
1576        final int[] viewIds = new int[numIds];
1577        for (int i = 0; i < numIds; i++) {
1578            viewIds[i] = autofillIds.get(i).getViewId();
1579        }
1580
1581        return viewIds;
1582    }
1583
1584    /**
1585     * Find a single view by its id.
1586     *
1587     * @param autofillId The autofill id of the view
1588     *
1589     * @return The view or {@code null} if view was not found
1590     */
1591    private View findView(@NonNull AutofillId autofillId) {
1592        final AutofillClient client = getClientLocked();
1593
1594        if (client == null) {
1595            return null;
1596        }
1597
1598        return client.findViewByAutofillIdTraversal(autofillId.getViewId());
1599    }
1600
1601    /** @hide */
1602    public boolean hasAutofillFeature() {
1603        return mService != null;
1604    }
1605
1606    /** @hide */
1607    public void onPendingSaveUi(int operation, IBinder token) {
1608        if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1609
1610        synchronized (mLock) {
1611            try {
1612                mService.onPendingSaveUi(operation, token);
1613            } catch (RemoteException e) {
1614                e.rethrowFromSystemServer();
1615            }
1616        }
1617    }
1618
1619    /** @hide */
1620    public void dump(String outerPrefix, PrintWriter pw) {
1621        pw.print(outerPrefix); pw.println("AutofillManager:");
1622        final String pfx = outerPrefix + "  ";
1623        pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
1624        pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
1625        pw.print(pfx); pw.print("context: "); pw.println(mContext);
1626        pw.print(pfx); pw.print("client: "); pw.println(getClientLocked());
1627        pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1628        pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1629        pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1630        pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1631        pw.print(pfx); pw.print("tracked views: ");
1632        if (mTrackedViews == null) {
1633            pw.println("null");
1634        } else {
1635            final String pfx2 = pfx + "  ";
1636            pw.println();
1637            pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1638            pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1639        }
1640        pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
1641        pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1642        pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
1643        pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1644        pw.print(" verbose: "); pw.println(sVerbose);
1645    }
1646
1647    private String getStateAsStringLocked() {
1648        switch (mState) {
1649            case STATE_UNKNOWN:
1650                return "STATE_UNKNOWN";
1651            case STATE_ACTIVE:
1652                return "STATE_ACTIVE";
1653            case STATE_FINISHED:
1654                return "STATE_FINISHED";
1655            case STATE_SHOWING_SAVE_UI:
1656                return "STATE_SHOWING_SAVE_UI";
1657            case STATE_DISABLED_BY_SERVICE:
1658                return "STATE_DISABLED_BY_SERVICE";
1659            default:
1660                return "INVALID:" + mState;
1661        }
1662    }
1663
1664    private boolean isActiveLocked() {
1665        return mState == STATE_ACTIVE;
1666    }
1667
1668    private boolean isDisabledByService() {
1669        return mState == STATE_DISABLED_BY_SERVICE;
1670    }
1671
1672    private void post(Runnable runnable) {
1673        final AutofillClient client = getClientLocked();
1674        if (client == null) {
1675            if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1676            return;
1677        }
1678        client.runOnUiThread(runnable);
1679    }
1680
1681    /**
1682     * View tracking information. Once all tracked views become invisible the session is finished.
1683     */
1684    private class TrackedViews {
1685        /** Visible tracked views */
1686        @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1687
1688        /** Invisible tracked views */
1689        @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1690
1691        /**
1692         * Check if set is null or value is in set.
1693         *
1694         * @param set   The set or null (== empty set)
1695         * @param value The value that might be in the set
1696         *
1697         * @return {@code true} iff set is not empty and value is in set
1698         */
1699        // TODO: move to Helper as static method
1700        private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1701            return set != null && set.contains(value);
1702        }
1703
1704        /**
1705         * Add a value to a set. If set is null, create a new set.
1706         *
1707         * @param set        The set or null (== empty set)
1708         * @param valueToAdd The value to add
1709         *
1710         * @return The set including the new value. If set was {@code null}, a set containing only
1711         *         the new value.
1712         */
1713        // TODO: move to Helper as static method
1714        @NonNull
1715        private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1716            if (set == null) {
1717                set = new ArraySet<>(1);
1718            }
1719
1720            set.add(valueToAdd);
1721
1722            return set;
1723        }
1724
1725        /**
1726         * Remove a value from a set.
1727         *
1728         * @param set           The set or null (== empty set)
1729         * @param valueToRemove The value to remove
1730         *
1731         * @return The set without the removed value. {@code null} if set was null, or is empty
1732         *         after removal.
1733         */
1734        // TODO: move to Helper as static method
1735        @Nullable
1736        private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1737            if (set == null) {
1738                return null;
1739            }
1740
1741            set.remove(valueToRemove);
1742
1743            if (set.isEmpty()) {
1744                return null;
1745            }
1746
1747            return set;
1748        }
1749
1750        /**
1751         * Set the tracked views.
1752         *
1753         * @param trackedIds The views to be tracked
1754         */
1755        TrackedViews(@Nullable AutofillId[] trackedIds) {
1756            final AutofillClient client = getClientLocked();
1757            if (trackedIds != null && client != null) {
1758                final boolean[] isVisible;
1759
1760                if (client.isVisibleForAutofill()) {
1761                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
1762                } else {
1763                    // All false
1764                    isVisible = new boolean[trackedIds.length];
1765                }
1766
1767                final int numIds = trackedIds.length;
1768                for (int i = 0; i < numIds; i++) {
1769                    final AutofillId id = trackedIds[i];
1770
1771                    if (isVisible[i]) {
1772                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1773                    } else {
1774                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1775                    }
1776                }
1777            }
1778
1779            if (sVerbose) {
1780                Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
1781                        + " mVisibleTrackedIds=" + mVisibleTrackedIds
1782                        + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1783            }
1784
1785            if (mVisibleTrackedIds == null) {
1786                finishSessionLocked();
1787            }
1788        }
1789
1790        /**
1791         * Called when a {@link View view's} visibility changes.
1792         *
1793         * @param id the id of the view/virtual view whose visibility changed.
1794         * @param isVisible visible if the view is visible in the view hierarchy.
1795         */
1796        void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
1797            AutofillClient client = getClientLocked();
1798
1799            if (sDebug) {
1800                Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
1801                        + isVisible);
1802            }
1803
1804            if (client != null && client.isVisibleForAutofill()) {
1805                if (isVisible) {
1806                    if (isInSet(mInvisibleTrackedIds, id)) {
1807                        mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1808                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1809                    }
1810                } else {
1811                    if (isInSet(mVisibleTrackedIds, id)) {
1812                        mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1813                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1814                    }
1815                }
1816            }
1817
1818            if (mVisibleTrackedIds == null) {
1819                if (sVerbose) {
1820                    Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1821                }
1822                finishSessionLocked();
1823            }
1824        }
1825
1826        /**
1827         * Called once the client becomes visible.
1828         *
1829         * @see AutofillClient#isVisibleForAutofill()
1830         */
1831        void onVisibleForAutofillLocked() {
1832            // The visibility of the views might have changed while the client was not be visible,
1833            // hence update the visibility state for all views.
1834            AutofillClient client = getClientLocked();
1835            ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1836            ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1837            if (client != null) {
1838                if (mInvisibleTrackedIds != null) {
1839                    final ArrayList<AutofillId> orderedInvisibleIds =
1840                            new ArrayList<>(mInvisibleTrackedIds);
1841                    final boolean[] isVisible = client.getViewVisibility(
1842                            getViewIds(orderedInvisibleIds));
1843
1844                    final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1845                    for (int i = 0; i < numInvisibleTrackedIds; i++) {
1846                        final AutofillId id = orderedInvisibleIds.get(i);
1847                        if (isVisible[i]) {
1848                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1849
1850                            if (sDebug) {
1851                                Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
1852                            }
1853                        } else {
1854                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1855                        }
1856                    }
1857                }
1858
1859                if (mVisibleTrackedIds != null) {
1860                    final ArrayList<AutofillId> orderedVisibleIds =
1861                            new ArrayList<>(mVisibleTrackedIds);
1862                    final boolean[] isVisible = client.getViewVisibility(
1863                            getViewIds(orderedVisibleIds));
1864
1865                    final int numVisibleTrackedIds = orderedVisibleIds.size();
1866                    for (int i = 0; i < numVisibleTrackedIds; i++) {
1867                        final AutofillId id = orderedVisibleIds.get(i);
1868
1869                        if (isVisible[i]) {
1870                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1871                        } else {
1872                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1873
1874                            if (sDebug) {
1875                                Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
1876                            }
1877                        }
1878                    }
1879                }
1880
1881                mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1882                mVisibleTrackedIds = updatedVisibleTrackedIds;
1883            }
1884
1885            if (mVisibleTrackedIds == null) {
1886                finishSessionLocked();
1887            }
1888        }
1889    }
1890
1891    /**
1892     * Callback for autofill related events.
1893     *
1894     * <p>Typically used for applications that display their own "auto-complete" views, so they can
1895     * enable / disable such views when the autofill UI is shown / hidden.
1896     */
1897    public abstract static class AutofillCallback {
1898
1899        /** @hide */
1900        @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN, EVENT_INPUT_UNAVAILABLE})
1901        @Retention(RetentionPolicy.SOURCE)
1902        public @interface AutofillEventType {}
1903
1904        /**
1905         * The autofill input UI associated with the view was shown.
1906         *
1907         * <p>If the view provides its own auto-complete UI and its currently shown, it
1908         * should be hidden upon receiving this event.
1909         */
1910        public static final int EVENT_INPUT_SHOWN = 1;
1911
1912        /**
1913         * The autofill input UI associated with the view was hidden.
1914         *
1915         * <p>If the view provides its own auto-complete UI that was hidden upon a
1916         * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1917         */
1918        public static final int EVENT_INPUT_HIDDEN = 2;
1919
1920        /**
1921         * The autofill input UI associated with the view isn't shown because
1922         * autofill is not available.
1923         *
1924         * <p>If the view provides its own auto-complete UI but was not displaying it
1925         * to avoid flickering, it could shown it upon receiving this event.
1926         */
1927        public static final int EVENT_INPUT_UNAVAILABLE = 3;
1928
1929        /**
1930         * Called after a change in the autofill state associated with a view.
1931         *
1932         * @param view view associated with the change.
1933         *
1934         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1935         */
1936        public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1937        }
1938
1939        /**
1940         * Called after a change in the autofill state associated with a virtual view.
1941         *
1942         * @param view parent view associated with the change.
1943         * @param virtualId id identifying the virtual child inside the parent view.
1944         *
1945         * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1946         */
1947        public void onAutofillEvent(@NonNull View view, int virtualId,
1948                @AutofillEventType int event) {
1949        }
1950    }
1951
1952    private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1953        private final WeakReference<AutofillManager> mAfm;
1954
1955        AutofillManagerClient(AutofillManager autofillManager) {
1956            mAfm = new WeakReference<>(autofillManager);
1957        }
1958
1959        @Override
1960        public void setState(int flags) {
1961            final AutofillManager afm = mAfm.get();
1962            if (afm != null) {
1963                afm.post(() -> afm.setState(flags));
1964            }
1965        }
1966
1967        @Override
1968        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
1969            final AutofillManager afm = mAfm.get();
1970            if (afm != null) {
1971                afm.post(() -> afm.autofill(sessionId, ids, values));
1972            }
1973        }
1974
1975        @Override
1976        public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1977                Intent fillInIntent) {
1978            final AutofillManager afm = mAfm.get();
1979            if (afm != null) {
1980                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
1981            }
1982        }
1983
1984        @Override
1985        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1986                Rect anchorBounds, IAutofillWindowPresenter presenter) {
1987            final AutofillManager afm = mAfm.get();
1988            if (afm != null) {
1989                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1990                        presenter));
1991            }
1992        }
1993
1994        @Override
1995        public void requestHideFillUi(int sessionId, AutofillId id) {
1996            final AutofillManager afm = mAfm.get();
1997            if (afm != null) {
1998                afm.post(() -> afm.requestHideFillUi(id));
1999            }
2000        }
2001
2002        @Override
2003        public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
2004            final AutofillManager afm = mAfm.get();
2005            if (afm != null) {
2006                afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
2007            }
2008        }
2009
2010        @Override
2011        public void startIntentSender(IntentSender intentSender, Intent intent) {
2012            final AutofillManager afm = mAfm.get();
2013            if (afm != null) {
2014                afm.post(() -> {
2015                    try {
2016                        afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
2017                    } catch (IntentSender.SendIntentException e) {
2018                        Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2019                    }
2020                });
2021            }
2022        }
2023
2024        @Override
2025        public void setTrackedViews(int sessionId, AutofillId[] ids,
2026                boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2027                AutofillId saveTriggerId) {
2028            final AutofillManager afm = mAfm.get();
2029            if (afm != null) {
2030                afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2031                        saveOnFinish, fillableIds, saveTriggerId));
2032            }
2033        }
2034
2035        @Override
2036        public void setSaveUiState(int sessionId, boolean shown) {
2037            final AutofillManager afm = mAfm.get();
2038            if (afm != null) {
2039                afm.post(() -> afm.setSaveUiState(sessionId, shown));
2040            }
2041        }
2042
2043        @Override
2044        public void setSessionFinished(int newState) {
2045            final AutofillManager afm = mAfm.get();
2046            if (afm != null) {
2047                afm.post(() -> afm.setSessionFinished(newState));
2048            }
2049        }
2050    }
2051}
2052