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