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