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