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