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