blob: d21cb3e64b8c65b44ae078e1cb5ac0499fed88b7 [file] [log] [blame]
Felipe Leme3461d3c2017-01-19 08:54:55 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view.autofill;
18
felipealfd3f8f62018-02-07 11:49:07 +010019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
22
Svetoslav Ganov24c90452017-12-27 15:17:14 -080023import android.accessibilityservice.AccessibilityServiceInfo;
Felipe Lemee6010f22017-03-03 11:19:51 -080024import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080025import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080026import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070027import android.annotation.RequiresFeature;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060028import android.annotation.SystemService;
Felipe Leme17292d12017-10-24 14:03:10 -070029import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080030import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000031import android.content.Intent;
32import android.content.IntentSender;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080033import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070036import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000037import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070038import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000039import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070041import android.service.autofill.AutofillService;
42import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080043import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070044import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070045import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080046import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070047import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080048import android.view.Choreographer;
Dake Gu6a20a192018-02-08 12:09:30 -080049import android.view.KeyEvent;
Felipe Leme3461d3c2017-01-19 08:54:55 -080050import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080051import android.view.accessibility.AccessibilityEvent;
52import android.view.accessibility.AccessibilityManager;
53import android.view.accessibility.AccessibilityNodeInfo;
54import android.view.accessibility.AccessibilityNodeProvider;
55import android.view.accessibility.AccessibilityWindowInfo;
felipealfd3f8f62018-02-07 11:49:07 +010056
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070057import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070058import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070059import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080060import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080061import com.android.internal.util.Preconditions;
felipealfd3f8f62018-02-07 11:49:07 +010062
Svetoslav Ganov24c90452017-12-27 15:17:14 -080063import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070064
Svetoslav Ganov24c90452017-12-27 15:17:14 -080065import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070066import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080067import java.lang.annotation.Retention;
68import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000069import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070070import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080071import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080072import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000073import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070074import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000075
Felipe Leme67e62092018-02-15 14:47:31 -080076//TODO: use java.lang.ref.Cleaner once Android supports Java 9
felipealfd3f8f62018-02-07 11:49:07 +010077import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080078
Felipe Leme3461d3c2017-01-19 08:54:55 -080079/**
Laura Davis43e75d92018-08-10 15:46:06 -070080 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
81 * integrate with the Autofill Framework lifecycle.
82 *
83 * <p>To learn about using Autofill in your app, read
84 * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
85 *
86 * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070087 *
Felipe Leme744976e2017-07-31 11:34:14 -070088 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
Laura Davis43e75d92018-08-10 15:46:06 -070089 * activity context. The autofill context is created when one of the following methods is called for
Felipe Leme744976e2017-07-31 11:34:14 -070090 * the first time in an activity context, and the current user has an enabled autofill service:
91 *
92 * <ul>
93 * <li>{@link #notifyViewEntered(View)}
94 * <li>{@link #notifyViewEntered(View, int, Rect)}
95 * <li>{@link #requestAutofill(View)}
96 * </ul>
97 *
Laura Davis43e75d92018-08-10 15:46:06 -070098 * <p>Typically, the context is automatically created when the first view of the activity is
Felipe Leme744976e2017-07-31 11:34:14 -070099 * focused because {@code View.onFocusChanged()} indirectly calls
100 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
101 * explicitly create it (for example, a custom view developer could offer a contextual menu action
102 * in a text-field view to let users manually request autofill).
103 *
104 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
105 * that represents the view hierarchy by calling
106 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
107 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
108 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
109 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
110 * the hierarchy.
111 *
112 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
113 * parses it looking for views that can be autofilled. If the service finds such views, it returns
114 * a data structure to the Android System containing the following optional info:
115 *
116 * <ul>
117 * <li>Datasets used to autofill subsets of views in the activity.
118 * <li>Id of views that the service can save their values for future autofilling.
119 * </ul>
120 *
121 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700122 * UI associated with the view, when the view is focused on and is part of a dataset.
123 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700124 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700125 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700126 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
127 *
128 * <p>When the service returns ids of savable views, the Android System keeps track of changes
129 * made to these views, so they can be used to determine if the autofill save UI is shown later.
130 *
131 * <p>The context is then finished when one of the following occurs:
132 *
133 * <ul>
134 * <li>{@link #commit()} is called or all savable views are gone.
135 * <li>{@link #cancel()} is called.
136 * </ul>
137 *
138 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700139 * shows an autofill save UI if the value of savable views have changed. If the user selects the
Felipe Leme744976e2017-07-31 11:34:14 -0700140 * option to Save, the current value of the views is then sent to the autofill service.
141 *
Laura Davis43e75d92018-08-10 15:46:06 -0700142 * <h3 id="additional-notes">Additional notes</h3>
143 *
144 * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800145 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600146@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700147@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800148public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800149
Felipe Leme640f30a2017-03-06 15:44:06 -0800150 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800151
Svet Ganov782043c2017-02-11 00:52:02 +0000152 /**
153 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700154 *
Svet Ganov782043c2017-02-11 00:52:02 +0000155 * <p>
156 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000157 */
158 public static final String EXTRA_ASSIST_STRUCTURE =
159 "android.view.autofill.extra.ASSIST_STRUCTURE";
160
161 /**
162 * Intent extra: The result of an authentication operation. It is
163 * either a fully populated {@link android.service.autofill.FillResponse}
164 * or a fully populated {@link android.service.autofill.Dataset} if
165 * a response or a dataset is being authenticated respectively.
166 *
167 * <p>
168 * Type: {@link android.service.autofill.FillResponse} or a
169 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000170 */
171 public static final String EXTRA_AUTHENTICATION_RESULT =
172 "android.view.autofill.extra.AUTHENTICATION_RESULT";
173
Felipe Leme7320ca92017-03-29 15:09:54 -0700174 /**
175 * Intent extra: The optional extras provided by the
176 * {@link android.service.autofill.AutofillService}.
177 *
178 * <p>For example, when the service responds to a {@link
179 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
180 * a {@code FillResponse} that requires authentication, the Intent that launches the
181 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700182 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700183 *
Felipe Lemea9372382017-10-09 14:42:36 -0700184 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
185 * can also add this bundle to the {@link Intent} set as the
186 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
187 * so the bundle can be recovered later on
188 * {@link android.service.autofill.SaveRequest#getClientState()}.
189 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700190 * <p>
191 * Type: {@link android.os.Bundle}
192 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700193 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600194 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700195
Felipe Lemec24a56a2017-08-03 14:27:57 -0700196 /** @hide */
197 public static final String EXTRA_RESTORE_SESSION_TOKEN =
198 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
199
200 private static final String SESSION_ID_TAG = "android:sessionId";
201 private static final String STATE_TAG = "android:state";
202 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
203
Felipe Leme0aa4c502017-04-26 12:36:01 -0700204 /** @hide */ public static final int ACTION_START_SESSION = 1;
205 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
206 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
207 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800208
Felipe Leme9f9ee252017-04-27 13:56:22 -0700209
210 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
211 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
212 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
213
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700214 /** Which bits in an authentication id are used for the dataset id */
215 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
216 /** How many bits in an authentication id are used for the dataset id */
217 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
218 /** @hide The index for an undefined data set */
219 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
220
221 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700222 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
223 *
224 * @hide
225 */
226 public static final int PENDING_UI_OPERATION_CANCEL = 1;
227
228 /**
229 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
230 *
231 * @hide
232 */
233 public static final int PENDING_UI_OPERATION_RESTORE = 2;
234
235 /**
236 * Initial state of the autofill context, set when there is no session (i.e., when
237 * {@link #mSessionId} is {@link #NO_SESSION}).
238 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700239 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
240 * the server.
241 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700242 * @hide
243 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700244 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700245
246 /**
247 * State where the autofill context hasn't been {@link #commit() finished} nor
248 * {@link #cancel() canceled} yet.
249 *
250 * @hide
251 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700252 public static final int STATE_ACTIVE = 1;
253
254 /**
255 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700256 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700257 *
258 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
259 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
260 *
261 * @hide
262 */
263 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700264
265 /**
266 * State where the autofill context has been {@link #commit() finished} but the server still has
267 * a session because the Save UI hasn't been dismissed yet.
268 *
269 * @hide
270 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700271 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700272
273 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700274 * State where the autofill is disabled because the service cannot autofill the activity at all.
275 *
276 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
277 * (and {@link #requestAutofill(View, int, Rect)}).
278 *
279 * @hide
280 */
281 public static final int STATE_DISABLED_BY_SERVICE = 4;
282
283 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700284 * Same as {@link #STATE_UNKNOWN}, but used on
285 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
286 * the URL bar changed on client mode
287 *
288 * @hide
289 */
290 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
291
292
293 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800294 * Timeout in ms for calls to the field classification service.
295 * @hide
296 */
297 public static final int FC_SERVICE_TIMEOUT = 5000;
298
299 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700300 * Makes an authentication id from a request id and a dataset id.
301 *
302 * @param requestId The request id.
303 * @param datasetId The dataset id.
304 * @return The authentication id.
305 * @hide
306 */
307 public static int makeAuthenticationId(int requestId, int datasetId) {
308 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
309 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
310 }
311
312 /**
313 * Gets the request id from an authentication id.
314 *
315 * @param authRequestId The authentication id.
316 * @return The request id.
317 * @hide
318 */
319 public static int getRequestIdFromAuthenticationId(int authRequestId) {
320 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
321 }
322
323 /**
324 * Gets the dataset id from an authentication id.
325 *
326 * @param authRequestId The authentication id.
327 * @return The dataset id.
328 * @hide
329 */
330 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
331 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
332 }
333
Felipe Leme4753bb02017-03-22 20:24:00 -0700334 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000335
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700336 /**
337 * There is currently no session running.
338 * {@hide}
339 */
340 public static final int NO_SESSION = Integer.MIN_VALUE;
341
Svet Ganov782043c2017-02-11 00:52:02 +0000342 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700343
344 private final Object mLock = new Object();
345
346 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000347 private IAutoFillManagerClient mServiceClient;
348
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700349 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900350 private Cleaner mServiceClientCleaner;
351
352 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800353 private AutofillCallback mCallback;
354
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700355 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000356
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700357 @GuardedBy("mLock")
358 private int mSessionId = NO_SESSION;
359
360 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700361 private int mState = STATE_UNKNOWN;
362
363 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000364 private boolean mEnabled;
365
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700366 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700367 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700368 @Nullable private ParcelableMap mLastAutofilledData;
369
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700370 /** If view tracking is enabled, contains the tracking state */
371 @GuardedBy("mLock")
372 @Nullable private TrackedViews mTrackedViews;
373
Felipe Leme27e20222017-05-18 15:24:11 -0700374 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
375 @GuardedBy("mLock")
376 @Nullable private ArraySet<AutofillId> mFillableIds;
377
Dake Gub0fa3782018-02-26 12:25:14 -0800378 /** id of last requested autofill ui */
379 @Nullable private AutofillId mIdShownFillUi;
380
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800381 /**
382 * Views that were already "entered" - if they're entered again when the session is not active,
383 * they're ignored
384 * */
385 @GuardedBy("mLock")
386 @Nullable private ArraySet<AutofillId> mEnteredIds;
387
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700388 /** If set, session is commited when the field is clicked. */
389 @GuardedBy("mLock")
390 @Nullable private AutofillId mSaveTriggerId;
391
Dake Gu67decfa2017-12-27 11:48:08 -0800392 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
393 @GuardedBy("mLock")
394 private boolean mOnInvisibleCalled;
395
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700396 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
397 @GuardedBy("mLock")
398 private boolean mSaveOnFinish;
399
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800400 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
401 @GuardedBy("mLock")
402 private CompatibilityBridge mCompatibilityBridge;
403
Svet Ganov782043c2017-02-11 00:52:02 +0000404 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800405 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000406 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000407 * Asks the client to start an authentication flow.
408 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700409 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000410 * @param intent The authentication intent.
411 * @param fillInIntent The authentication fill-in intent.
412 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800413 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700414 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800415
416 /**
417 * Tells the client this manager has state to be reset.
418 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800419 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700420
421 /**
422 * Request showing the autofill UI.
423 *
424 * @param anchor The real view the UI needs to anchor to.
425 * @param width The width of the fill UI content.
426 * @param height The height of the fill UI content.
427 * @param virtualBounds The bounds of the virtual decendant of the anchor.
428 * @param presenter The presenter that controls the fill UI window.
429 * @return Whether the UI was shown.
430 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800431 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700432 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
433
434 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800435 * Dispatch unhandled keyevent from Autofill window
436 * @param anchor The real view the UI needs to anchor to.
437 * @param keyEvent Unhandled KeyEvent from autofill window.
438 */
439 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
440
441 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700442 * Request hiding the autofill UI.
443 *
444 * @return Whether the UI was hidden.
445 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800446 boolean autofillClientRequestHideFillUi();
447
448 /**
449 * Gets whether the fill UI is currenlty being shown.
450 *
451 * @return Whether the fill UI is currently being shown
452 */
453 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700454
455 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700456 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700457 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700458 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700459 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800460 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700461
462 /**
463 * Checks is the client is currently visible as understood by autofill.
464 *
465 * @return {@code true} if the client is currently visible
466 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800467 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700468
469 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800470 * Client might disable enter/exit event e.g. when activity is paused.
471 */
472 boolean isDisablingEnterExitEventForAutofill();
473
474 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700475 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700476 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800477 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700478 *
Felipe Leme27e20222017-05-18 15:24:11 -0700479 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700480 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800481 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
482 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700483
484 /**
485 * Finds a view by traversing the hierarchies of the client.
486 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800487 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700488 *
489 * @return The view, or {@code null} if not found
490 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800491 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
492
493 /**
494 * Finds a view by a11y id in a given client window.
495 *
496 * @param viewId The accessibility id of the views to find
497 * @param windowId The accessibility window id where to search
498 *
499 * @return The view, or {@code null} if not found
500 */
501 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700502
503 /**
504 * Runs the specified action on the UI thread.
505 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800506 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700507
508 /**
509 * Gets the complete component name of this client.
510 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800511 ComponentName autofillClientGetComponentName();
512
513 /**
514 * Gets the activity token
515 */
516 @Nullable IBinder autofillClientGetActivityToken();
517
518 /**
519 * @return Whether compatibility mode is enabled.
520 */
Felipe Leme42b97932018-02-20 13:04:31 -0800521 boolean autofillClientIsCompatibilityModeEnabled();
522
523 /**
524 * Gets the next unique autofill ID.
525 *
526 * <p>Typically used to manage views whose content is recycled - see
527 * {@link View#setAutofillId(AutofillId)} for more info.
528 *
529 * @return An ID that is unique in the activity.
530 */
531 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000532 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800533
534 /**
535 * @hide
536 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800537 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800538 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800539 mService = service;
540 }
541
542 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800543 * @hide
544 */
545 public void enableCompatibilityMode() {
546 synchronized (mLock) {
547 // The accessibility manager is a singleton so we may need to plug
548 // different bridge based on which activity is currently focused
549 // in the current process. Since compat would be rarely used, just
550 // create and register a new instance every time.
551 mCompatibilityBridge = new CompatibilityBridge();
552 }
553 }
554
555 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700556 * Restore state after activity lifecycle
557 *
558 * @param savedInstanceState The state to be restored
559 *
560 * {@hide}
561 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700562 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700563 if (!hasAutofillFeature()) {
564 return;
565 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700566 synchronized (mLock) {
567 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
568
Felipe Lemec24a56a2017-08-03 14:27:57 -0700569 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700570 Log.w(TAG, "New session was started before onCreate()");
571 return;
572 }
573
574 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700575 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700576
577 if (mSessionId != NO_SESSION) {
578 ensureServiceClientAddedIfNeededLocked();
579
Felipe Leme637e05e2017-12-06 12:09:37 -0800580 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700581 if (client != null) {
582 try {
583 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800584 client.autofillClientGetActivityToken(),
585 mServiceClient.asBinder());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700586
587 if (!sessionWasRestored) {
588 Log.w(TAG, "Session " + mSessionId + " could not be restored");
589 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700590 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700591 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700592 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700593 Log.d(TAG, "session " + mSessionId + " was restored");
594 }
595
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800596 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700597 }
598 } catch (RemoteException e) {
599 Log.e(TAG, "Could not figure out if there was an autofill session", e);
600 }
601 }
602 }
603 }
604 }
605
606 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700607 * Called once the client becomes visible.
608 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800609 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700610 *
611 * {@hide}
612 */
613 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800614 // This gets called when the client just got visible at which point the visibility
615 // of the tracked views may not have been computed (due to a pending layout, etc).
616 // While generally we have no way to know when the UI has settled. We will evaluate
617 // the tracked views state at the end of next frame to guarantee that everything
618 // that may need to be laid out is laid out.
619 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
620 synchronized (mLock) {
621 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
622 mTrackedViews.onVisibleForAutofillChangedLocked();
623 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700624 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800625 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700626 }
627
628 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800629 * Called once the client becomes invisible.
630 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800631 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800632 *
633 * {@hide}
634 */
635 public void onInvisibleForAutofill() {
636 synchronized (mLock) {
637 mOnInvisibleCalled = true;
638 }
639 }
640
641 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700642 * Save state before activity lifecycle
643 *
644 * @param outState Place to store the state
645 *
646 * {@hide}
647 */
648 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700649 if (!hasAutofillFeature()) {
650 return;
651 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700652 synchronized (mLock) {
653 if (mSessionId != NO_SESSION) {
654 outState.putInt(SESSION_ID_TAG, mSessionId);
655 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700656 if (mState != STATE_UNKNOWN) {
657 outState.putInt(STATE_TAG, mState);
658 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700659 if (mLastAutofilledData != null) {
660 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
661 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700662 }
663 }
664
665 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800666 * @hide
667 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800668 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100669 public boolean isCompatibilityModeEnabledLocked() {
670 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800671 }
672
673 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700674 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700675 *
676 * <p>Typically used to determine whether the option to explicitly request autofill should
677 * be offered - see {@link #requestAutofill(View)}.
678 *
679 * @return whether autofill is enabled for the current user.
680 */
681 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800682 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700683 return false;
684 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700685 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800686 if (isDisabledByServiceLocked()) {
687 return false;
688 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700689 ensureServiceClientAddedIfNeededLocked();
690 return mEnabled;
691 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700692 }
693
694 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700695 * Should always be called from {@link AutofillService#getFillEventHistory()}.
696 *
697 * @hide
698 */
699 @Nullable public FillEventHistory getFillEventHistory() {
700 try {
701 return mService.getFillEventHistory();
702 } catch (RemoteException e) {
703 e.rethrowFromSystemServer();
704 return null;
705 }
706 }
707
708 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700709 * Explicitly requests a new autofill context.
710 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700711 * <p>Normally, the autofill context is automatically started if necessary when
712 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
713 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
714 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700715 *
716 * @param view view requesting the new autofill context.
717 */
718 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700719 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700720 }
721
722 /**
723 * Explicitly requests a new autofill context for virtual views.
724 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700725 * <p>Normally, the autofill context is automatically started if necessary when
726 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
727 * cases where it must be explicitly started. For example, when the virtual view offers an
728 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700729 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700730 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
731 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
732 * the absolute bounds could be calculated by:
733 *
734 * <pre class="prettyprint">
735 * int offset[] = new int[2];
736 * getLocationOnScreen(offset);
737 * Rect absBounds = new Rect(bounds.left + offset[0],
738 * bounds.top + offset[1],
739 * bounds.right + offset[0], bounds.bottom + offset[1]);
740 * </pre>
741 *
742 * @param view the virtual view parent.
743 * @param virtualId id identifying the virtual child inside the parent view.
744 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700745 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700746 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
747 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700748 }
749
Felipe Leme2ac463e2017-03-13 14:06:25 -0700750 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700751 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800752 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700753 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800754 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700755 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700756 notifyViewEntered(view, 0);
757 }
758
Andreas Gampe3f24e692018-02-05 13:24:28 -0800759 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800760 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800761 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700762 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800763 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
764 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700765 }
766 return true;
767 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800768 if (isFinishedLocked()) {
769 // Session already finished: ignore if automatic request and view already entered
770 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
771 && mEnteredIds.contains(id)) {
772 if (sVerbose) {
773 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
774 + ") on state " + getStateAsStringLocked()
775 + " because view was already entered: " + mEnteredIds);
776 }
777 return true;
778 }
779 }
780 if (sVerbose) {
781 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
782 + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
Felipe Leme17292d12017-10-24 14:03:10 -0700783 }
784 return false;
785 }
786
Dake Gu67decfa2017-12-27 11:48:08 -0800787 private boolean isClientVisibleForAutofillLocked() {
788 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800789 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800790 }
791
792 private boolean isClientDisablingEnterExitEvent() {
793 final AutofillClient client = getClient();
794 return client != null && client.isDisablingEnterExitEventForAutofill();
795 }
796
Felipe Lemed1146422017-04-26 10:17:05 -0700797 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700798 if (!hasAutofillFeature()) {
799 return;
800 }
Dake Gu67decfa2017-12-27 11:48:08 -0800801 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700802 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800803 callback = notifyViewEnteredLocked(view, flags);
804 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700805
Dake Gu67decfa2017-12-27 11:48:08 -0800806 if (callback != null) {
807 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
808 }
809 }
Svet Ganov782043c2017-02-11 00:52:02 +0000810
Dake Gu67decfa2017-12-27 11:48:08 -0800811 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800812 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800813 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800814 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800815 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800816
817 AutofillCallback callback = null;
818
819 ensureServiceClientAddedIfNeededLocked();
820
821 if (!mEnabled) {
822 if (mCallback != null) {
823 callback = mCallback;
824 }
825 } else {
826 // don't notify entered when Activity is already in background
827 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700828 final AutofillValue value = view.getAutofillValue();
829
Felipe Lemec24a56a2017-08-03 14:27:57 -0700830 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700831 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700832 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700833 } else {
834 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700835 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700836 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800837 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700838 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800839 }
Dake Gu67decfa2017-12-27 11:48:08 -0800840 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800841 }
842
843 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700844 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800845 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700846 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800847 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700848 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700849 if (!hasAutofillFeature()) {
850 return;
851 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700852 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800853 notifyViewExitedLocked(view);
854 }
855 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800856
Andreas Gampe3f24e692018-02-05 13:24:28 -0800857 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800858 void notifyViewExitedLocked(@NonNull View view) {
859 ensureServiceClientAddedIfNeededLocked();
860
861 if (mEnabled && isActiveLocked()) {
862 // dont notify exited when Activity is already in background
863 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -0800864 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800865
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700866 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700867 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700868 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800869 }
870 }
871
872 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700873 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700874 *
875 * @param view {@link View} that was exited.
876 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700877 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700878 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
879 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
880 }
881
882 /**
883 * Called when a virtual view's visibility changed.
884 *
885 * @param view {@link View} that was exited.
886 * @param virtualId id identifying the virtual child inside the parent view.
887 * @param isVisible visible if the view is visible in the view hierarchy.
888 */
889 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
890 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
891 }
892
893 /**
894 * Called when a view/virtual view's visibility changed.
895 *
896 * @param view {@link View} that was exited.
897 * @param virtualId id identifying the virtual child inside the parent view.
898 * @param isVisible visible if the view is visible in the view hierarchy.
899 * @param virtual Whether the view is virtual.
900 */
901 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
902 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700903 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700904 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700905 final AutofillId id = virtual ? getAutofillId(view, virtualId)
906 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -0800907 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700908 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700909 if (mFillableIds.contains(id)) {
910 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
911 requestHideFillUi(id, view);
912 }
913 }
914 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800915 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -0800916 } else if (sVerbose) {
917 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -0700918 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700919 }
920 }
921 }
922
923 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700924 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800925 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700926 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
927 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
928 * the absolute bounds could be calculated by:
929 *
930 * <pre class="prettyprint">
931 * int offset[] = new int[2];
932 * getLocationOnScreen(offset);
933 * Rect absBounds = new Rect(bounds.left + offset[0],
934 * bounds.top + offset[1],
935 * bounds.right + offset[0], bounds.bottom + offset[1]);
936 * </pre>
937 *
938 * @param view the virtual view parent.
939 * @param virtualId id identifying the virtual child inside the parent view.
940 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800941 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700942 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
943 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700944 }
945
Felipe Leme6dcec872017-05-25 11:24:23 -0700946 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700947 if (!hasAutofillFeature()) {
948 return;
949 }
Dake Gu67decfa2017-12-27 11:48:08 -0800950 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700951 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800952 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
953 }
Felipe Leme17292d12017-10-24 14:03:10 -0700954
Dake Gu67decfa2017-12-27 11:48:08 -0800955 if (callback != null) {
956 callback.onAutofillEvent(view, virtualId,
957 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
958 }
959 }
Svet Ganov782043c2017-02-11 00:52:02 +0000960
Dake Gu67decfa2017-12-27 11:48:08 -0800961 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800962 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800963 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
964 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800965 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -0800966 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800967 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -0800968
969 ensureServiceClientAddedIfNeededLocked();
970
971 if (!mEnabled) {
972 if (mCallback != null) {
973 callback = mCallback;
974 }
975 } else {
976 // don't notify entered when Activity is already in background
977 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700978 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700979 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700980 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700981 } else {
982 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700983 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700984 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800985 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700986 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800987 }
Dake Gu67decfa2017-12-27 11:48:08 -0800988 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800989 }
990
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800991 @GuardedBy("mLock")
992 private void addEnteredIdLocked(@NonNull AutofillId id) {
993 if (mEnteredIds == null) {
994 mEnteredIds = new ArraySet<>(1);
995 }
996 mEnteredIds.add(id);
997 }
998
Philip P. Moltmann44611812017-02-23 12:52:46 -0800999 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001000 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001001 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001002 * @param view the virtual view parent.
1003 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001004 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001005 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001006 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001007 if (!hasAutofillFeature()) {
1008 return;
1009 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001010 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001011 notifyViewExitedLocked(view, virtualId);
1012 }
1013 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001014
Andreas Gampe3f24e692018-02-05 13:24:28 -08001015 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001016 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1017 ensureServiceClientAddedIfNeededLocked();
1018
1019 if (mEnabled && isActiveLocked()) {
1020 // don't notify exited when Activity is already in background
1021 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -07001022 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001023
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001024 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001025 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001026 }
Svet Ganov782043c2017-02-11 00:52:02 +00001027 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001028 }
1029
1030 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001031 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001032 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001033 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001034 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001035 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001036 if (!hasAutofillFeature()) {
1037 return;
1038 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001039 AutofillId id = null;
1040 boolean valueWasRead = false;
1041 AutofillValue value = null;
1042
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001043 synchronized (mLock) {
1044 // If the session is gone some fields might still be highlighted, hence we have to
1045 // remove the isAutofilled property even if no sessions are active.
1046 if (mLastAutofilledData == null) {
1047 view.setAutofilled(false);
1048 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001049 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001050 if (mLastAutofilledData.containsKey(id)) {
1051 value = view.getAutofillValue();
1052 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001053
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001054 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1055 view.setAutofilled(true);
1056 } else {
1057 view.setAutofilled(false);
1058 mLastAutofilledData.remove(id);
1059 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001060 } else {
1061 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001062 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001063 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001064
Felipe Lemec24a56a2017-08-03 14:27:57 -07001065 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001066 if (sVerbose) {
1067 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1068 + "): ignoring on state " + getStateAsStringLocked());
Felipe Lemec7b45292017-09-19 09:06:20 -07001069 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001070 return;
1071 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001072
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001073 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001074 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001075 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001076
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001077 if (!valueWasRead) {
1078 value = view.getAutofillValue();
1079 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001080
Felipe Leme0aa4c502017-04-26 12:36:01 -07001081 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001082 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001083 }
1084
Felipe Lemebab851c2017-02-03 18:45:08 -08001085 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001086 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001087 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001088 * @param view the virtual view parent.
1089 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001090 * @param value new value of the child.
1091 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001092 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001093 if (!hasAutofillFeature()) {
1094 return;
1095 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001096 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001097 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001098 if (sVerbose) {
1099 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1100 + "): ignoring on state " + getStateAsStringLocked());
1101 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001102 return;
1103 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001104
Felipe Leme6dcec872017-05-25 11:24:23 -07001105 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001106 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001107 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001108 }
1109
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001110 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001111 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001112 *
Felipe Leme67e62092018-02-15 14:47:31 -08001113 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001114 */
Felipe Leme67e62092018-02-15 14:47:31 -08001115 public void notifyViewClicked(@NonNull View view) {
1116 notifyViewClicked(view.getAutofillId());
1117 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001118
Felipe Leme67e62092018-02-15 14:47:31 -08001119 /**
1120 * Called to indicate a virtual view has been clicked.
1121 *
1122 * @param view the virtual view parent.
1123 * @param virtualId id identifying the virtual child inside the parent view.
1124 */
1125 public void notifyViewClicked(@NonNull View view, int virtualId) {
1126 notifyViewClicked(getAutofillId(view, virtualId));
1127 }
1128
1129 private void notifyViewClicked(AutofillId id) {
1130 if (!hasAutofillFeature()) {
1131 return;
1132 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001133 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1134
1135 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001136 if (!mEnabled || !isActiveLocked()) {
1137 return;
1138 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001139 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1140 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1141 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001142 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001143 }
1144 }
1145 }
1146
1147 /**
1148 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1149 *
1150 * @hide
1151 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001152 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001153 if (!hasAutofillFeature()) {
1154 return;
1155 }
1156 synchronized (mLock) {
1157 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001158 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001159 commitLocked();
1160 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001161 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001162 cancelLocked();
1163 }
1164 }
1165 }
1166
Felipe Lemebab851c2017-02-03 18:45:08 -08001167 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001168 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001169 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001170 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1171 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1172 * that represent the HTML elements, it should call this method after the form is submitted and
1173 * another page is rendered.
1174 *
1175 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1176 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001177 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001178 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001179 if (!hasAutofillFeature()) {
1180 return;
1181 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001182 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001183 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001184 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001185 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001186 }
1187
Andreas Gampe3f24e692018-02-05 13:24:28 -08001188 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001189 private void commitLocked() {
1190 if (!mEnabled && !isActiveLocked()) {
1191 return;
1192 }
1193 finishSessionLocked();
1194 }
1195
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001196 /**
1197 * Called to indicate the current autofill context should be cancelled.
1198 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001199 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1200 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1201 * that represent the HTML elements, it should call this method if the user does not post the
1202 * form but moves to another form in this page.
1203 *
1204 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1205 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001206 */
1207 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001208 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001209 if (!hasAutofillFeature()) {
1210 return;
1211 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001212 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001213 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001214 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001215 }
1216
Andreas Gampe3f24e692018-02-05 13:24:28 -08001217 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001218 private void cancelLocked() {
1219 if (!mEnabled && !isActiveLocked()) {
1220 return;
1221 }
1222 cancelSessionLocked();
1223 }
1224
Svet Ganovf965b872017-04-28 16:34:02 -07001225 /** @hide */
1226 public void disableOwnedAutofillServices() {
1227 disableAutofillServices();
1228 }
1229
Svet Ganovf20a0372017-04-10 17:08:05 -07001230 /**
1231 * If the app calling this API has enabled autofill services they
1232 * will be disabled.
1233 */
Svet Ganovf965b872017-04-28 16:34:02 -07001234 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001235 if (!hasAutofillFeature()) {
1236 return;
1237 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001238 try {
1239 mService.disableOwnedAutofillServices(mContext.getUserId());
1240 } catch (RemoteException e) {
1241 throw e.rethrowFromSystemServer();
1242 }
1243 }
1244
Felipe Lemedb041182017-04-21 17:33:38 -07001245 /**
1246 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1247 * enabled for the current user, or {@code false} otherwise.
1248 */
1249 public boolean hasEnabledAutofillServices() {
1250 if (mService == null) return false;
1251
1252 try {
1253 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1254 } catch (RemoteException e) {
1255 throw e.rethrowFromSystemServer();
1256 }
1257 }
1258
1259 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001260 * Returns the component name of the {@link AutofillService} that is enabled for the current
1261 * user.
1262 */
1263 @Nullable
1264 public ComponentName getAutofillServiceComponentName() {
1265 if (mService == null) return null;
1266
1267 try {
1268 return mService.getAutofillServiceComponentName();
1269 } catch (RemoteException e) {
1270 throw e.rethrowFromSystemServer();
1271 }
1272 }
1273
1274 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001275 * Gets the id of the {@link UserData} used for
1276 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1277 *
1278 * <p>This method is useful when the service must check the status of the {@link UserData} in
1279 * the device without fetching the whole object.
1280 *
1281 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1282 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1283 * the user.
1284 *
1285 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1286 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1287 * service for the user.
1288 */
1289 @Nullable public String getUserDataId() {
1290 try {
1291 return mService.getUserDataId();
1292 } catch (RemoteException e) {
1293 e.rethrowFromSystemServer();
1294 return null;
1295 }
1296 }
1297
1298 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001299 * Gets the user data used for
1300 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001301 *
Felipe Leme27f45732017-12-22 09:05:22 -08001302 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1303 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1304 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001305 *
Felipe Leme452886a2017-11-27 13:09:13 -08001306 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1307 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001308 */
Felipe Leme452886a2017-11-27 13:09:13 -08001309 @Nullable public UserData getUserData() {
1310 try {
1311 return mService.getUserData();
1312 } catch (RemoteException e) {
1313 e.rethrowFromSystemServer();
1314 return null;
1315 }
1316 }
1317
1318 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001319 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001320 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001321 *
1322 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1323 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1324 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001325 */
Felipe Leme452886a2017-11-27 13:09:13 -08001326 public void setUserData(@Nullable UserData userData) {
1327 try {
1328 mService.setUserData(userData);
1329 } catch (RemoteException e) {
1330 e.rethrowFromSystemServer();
1331 }
1332 }
1333
1334 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001335 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1336 * enabled.
1337 *
1338 * <p>As field classification is an expensive operation, it could be disabled, either
1339 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1340 * permanently (for example, because the device is a low-level device).
1341 *
1342 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1343 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1344 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001345 */
Felipe Leme329d0402017-12-06 09:22:43 -08001346 public boolean isFieldClassificationEnabled() {
1347 try {
1348 return mService.isFieldClassificationEnabled();
1349 } catch (RemoteException e) {
1350 e.rethrowFromSystemServer();
1351 return false;
1352 }
1353 }
1354
1355 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001356 * Gets the name of the default algorithm used for
1357 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1358 *
1359 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1360 * set.
1361 *
1362 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1363 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1364 * the user.
1365 */
1366 @Nullable
1367 public String getDefaultFieldClassificationAlgorithm() {
1368 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001369 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001370 } catch (RemoteException e) {
1371 e.rethrowFromSystemServer();
1372 return null;
1373 }
1374 }
1375
1376 /**
1377 * Gets the name of all algorithms currently available for
1378 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1379 *
1380 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001381 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1382 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001383 */
1384 @NonNull
1385 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001386 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001387 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001388 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1389 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001390 } catch (RemoteException e) {
1391 e.rethrowFromSystemServer();
1392 return null;
1393 }
1394 }
1395
1396 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001397 * Returns {@code true} if autofill is supported by the current device and
1398 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001399 *
1400 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1401 * <ol>
1402 * <li>Low-end devices.
1403 * <li>Device policy rules that forbid its usage.
1404 * </ol>
1405 */
1406 public boolean isAutofillSupported() {
1407 if (mService == null) return false;
1408
1409 try {
1410 return mService.isServiceSupported(mContext.getUserId());
1411 } catch (RemoteException e) {
1412 throw e.rethrowFromSystemServer();
1413 }
1414 }
1415
Felipe Leme637e05e2017-12-06 12:09:37 -08001416 // Note: don't need to use locked suffix because mContext is final.
1417 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001418 final AutofillClient client = mContext.getAutofillClient();
1419 if (client == null && sDebug) {
1420 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1421 + mContext);
1422 }
1423 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001424 }
1425
Dake Gud9dbd272018-03-13 11:38:42 -07001426 /**
1427 * Check if autofill ui is showing, must be called on UI thread.
1428 * @hide
1429 */
1430 public boolean isAutofillUiShowing() {
1431 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001432 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001433 }
1434
Svet Ganov782043c2017-02-11 00:52:02 +00001435 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001436 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001437 if (!hasAutofillFeature()) {
1438 return;
1439 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001440 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001441 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1442 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1443 // service set the extra and returned RESULT_CANCELED...
1444
Felipe Leme9f9ee252017-04-27 13:56:22 -07001445 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001446
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001447 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001448 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001449 return;
1450 }
Dake Gu67decfa2017-12-27 11:48:08 -08001451 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1452 // of app activity. We enforce enter event to re-show fill ui in such case.
1453 // CTS example:
1454 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1455 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1456 if (!mOnInvisibleCalled && focusView != null
1457 && focusView.canNotifyAutofillEnterExitEvent()) {
1458 notifyViewExitedLocked(focusView);
1459 notifyViewEnteredLocked(focusView, 0);
1460 }
1461 if (data == null) {
1462 // data is set to null when result is not RESULT_OK
1463 return;
1464 }
1465
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001466 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1467 final Bundle responseData = new Bundle();
1468 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001469 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1470 if (newClientState != null) {
1471 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1472 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001473 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001474 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1475 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001476 } catch (RemoteException e) {
1477 Log.e(TAG, "Error delivering authentication result", e);
1478 }
Svet Ganov782043c2017-02-11 00:52:02 +00001479 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001480 }
1481
Felipe Leme42b97932018-02-20 13:04:31 -08001482 /**
1483 * Gets the next unique autofill ID for the activity context.
1484 *
1485 * <p>Typically used to manage views whose content is recycled - see
1486 * {@link View#setAutofillId(AutofillId)} for more info.
1487 *
1488 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1489 * the {@link Context} associated with this {@link AutofillManager}.
1490 */
1491 @Nullable
1492 public AutofillId getNextAutofillId() {
1493 final AutofillClient client = getClient();
1494 if (client == null) return null;
1495
1496 final AutofillId id = client.autofillClientGetNextAutofillId();
1497
1498 if (id == null && sDebug) {
1499 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1500 }
1501
1502 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001503 }
1504
Felipe Leme6dcec872017-05-25 11:24:23 -07001505 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001506 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001507 }
1508
Andreas Gampe3f24e692018-02-05 13:24:28 -08001509 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001510 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1511 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001512 if (sVerbose) {
1513 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001514 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001515 + ", compatMode=" + isCompatibilityModeEnabledLocked()
1516 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001517 }
Felipe Leme3103c632017-12-18 15:05:14 -08001518 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001519 if (sVerbose) {
1520 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001521 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001522 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001523 return;
1524 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001525 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001526 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001527 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001528
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001529 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001530 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme185de722018-02-13 17:25:44 -08001531 mCallback != null, flags, client.autofillClientGetComponentName(),
1532 isCompatibilityModeEnabledLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001533 if (mSessionId != NO_SESSION) {
1534 mState = STATE_ACTIVE;
1535 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001536 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001537 } catch (RemoteException e) {
1538 throw e.rethrowFromSystemServer();
1539 }
1540 }
1541
Andreas Gampe3f24e692018-02-05 13:24:28 -08001542 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001543 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001544 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001545
1546 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001547
Svet Ganov782043c2017-02-11 00:52:02 +00001548 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001549 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001550 } catch (RemoteException e) {
1551 throw e.rethrowFromSystemServer();
1552 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001553
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001554 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001555 }
1556
Andreas Gampe3f24e692018-02-05 13:24:28 -08001557 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001558 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001559 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001560
1561 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001562
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001563 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001564 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001565 } catch (RemoteException e) {
1566 throw e.rethrowFromSystemServer();
1567 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001568
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001569 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001570 }
1571
Andreas Gampe3f24e692018-02-05 13:24:28 -08001572 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001573 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001574 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001575 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001576 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001577 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001578 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001579 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001580 if (resetEnteredIds) {
1581 mEnteredIds = null;
1582 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001583 }
1584
Andreas Gampe3f24e692018-02-05 13:24:28 -08001585 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001586 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1587 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001588 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001589 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1590 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001591 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001592 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1593
Felipe Leme3461d3c2017-01-19 08:54:55 -08001594 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001595 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001596 final AutofillClient client = getClient();
1597 if (client == null) return; // NOTE: getClient() already logd it..
1598
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001599 final int newId = mService.updateOrRestartSession(
1600 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001601 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001602 mCallback != null, flags, client.autofillClientGetComponentName(),
Felipe Leme185de722018-02-13 17:25:44 -08001603 mSessionId, action, isCompatibilityModeEnabledLocked());
Felipe Leme7f33cd32017-05-11 10:10:49 -07001604 if (newId != mSessionId) {
1605 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1606 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001607 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001608 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001609 }
1610 } else {
1611 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1612 mContext.getUserId());
1613 }
1614
Felipe Leme3461d3c2017-01-19 08:54:55 -08001615 } catch (RemoteException e) {
1616 throw e.rethrowFromSystemServer();
1617 }
1618 }
Svet Ganov782043c2017-02-11 00:52:02 +00001619
Andreas Gampe3f24e692018-02-05 13:24:28 -08001620 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001621 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001622 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001623 return;
1624 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001625
Svet Ganov782043c2017-02-11 00:52:02 +00001626 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001627 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001628 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001629 final int userId = mContext.getUserId();
1630 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001631 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1632 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1633 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001634 final IAutoFillManager service = mService;
1635 final IAutoFillManagerClient serviceClient = mServiceClient;
1636 mServiceClientCleaner = Cleaner.create(this, () -> {
1637 try {
1638 service.removeClient(serviceClient, userId);
1639 } catch (RemoteException e) {
1640 }
1641 });
Svet Ganov782043c2017-02-11 00:52:02 +00001642 } catch (RemoteException e) {
1643 throw e.rethrowFromSystemServer();
1644 }
1645 }
1646 }
1647
Felipe Lemee6010f22017-03-03 11:19:51 -08001648 /**
1649 * Registers a {@link AutofillCallback} to receive autofill events.
1650 *
1651 * @param callback callback to receive events.
1652 */
1653 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001654 if (!hasAutofillFeature()) {
1655 return;
1656 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001657 synchronized (mLock) {
1658 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001659
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001660 final boolean hadCallback = mCallback != null;
1661 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001662
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001663 if (!hadCallback) {
1664 try {
1665 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1666 } catch (RemoteException e) {
1667 throw e.rethrowFromSystemServer();
1668 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001669 }
1670 }
1671 }
1672
1673 /**
1674 * Unregisters a {@link AutofillCallback} to receive autofill events.
1675 *
1676 * @param callback callback to stop receiving events.
1677 */
1678 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001679 if (!hasAutofillFeature()) {
1680 return;
1681 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001682 synchronized (mLock) {
1683 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001684
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001685 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001686
Felipe Lemee6010f22017-03-03 11:19:51 -08001687 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001688 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001689 } catch (RemoteException e) {
1690 throw e.rethrowFromSystemServer();
1691 }
1692 }
1693 }
1694
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001695 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1696 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1697 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001698 if (anchor == null) {
1699 return;
1700 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001701
1702 AutofillCallback callback = null;
1703 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001704 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001705 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001706
1707 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001708 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08001709 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001710 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08001711 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001712 }
1713 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001714 }
1715 }
1716
1717 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001718 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001719 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001720 AutofillCallback.EVENT_INPUT_SHOWN);
1721 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001722 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001723 }
1724 }
1725 }
1726
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001727 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1728 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001729 synchronized (mLock) {
1730 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001731 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001732 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001733 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1734 // before onAuthenticationResult()
1735 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001736 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001737 }
1738 }
1739 }
1740 }
1741
Dake Gu6a20a192018-02-08 12:09:30 -08001742 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
1743 final View anchor = findView(id);
1744 if (anchor == null) {
1745 return;
1746 }
1747
1748 AutofillCallback callback = null;
1749 synchronized (mLock) {
1750 if (mSessionId == sessionId) {
1751 AutofillClient client = getClient();
1752
1753 if (client != null) {
1754 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
1755 }
1756 }
1757 }
1758 }
1759
Felipe Leme51e29da2017-10-24 14:03:10 -07001760 /** @hide */
1761 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1762 /** @hide */
1763 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1764 /** @hide */
1765 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1766 /** @hide */
1767 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1768 /** @hide */
1769 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1770
1771 private void setState(int flags) {
1772 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001773 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001774 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1775 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001776 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001777 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001778 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001779 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001780 // Reset connection to system
1781 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001782 if (mServiceClientCleaner != null) {
1783 mServiceClientCleaner.clean();
1784 mServiceClientCleaner = null;
1785 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001786 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001787 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001788 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1789 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001790 }
1791
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001792 /**
1793 * Sets a view as autofilled if the current value is the {code targetValue}.
1794 *
1795 * @param view The view that is to be autofilled
1796 * @param targetValue The value we want to fill into view
1797 */
1798 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1799 AutofillValue currentValue = view.getAutofillValue();
1800 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001801 synchronized (mLock) {
1802 if (mLastAutofilledData == null) {
1803 mLastAutofilledData = new ParcelableMap(1);
1804 }
Felipe Leme42b97932018-02-20 13:04:31 -08001805 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001806 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001807 view.setAutofilled(true);
1808 }
1809 }
1810
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001811 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001812 synchronized (mLock) {
1813 if (sessionId != mSessionId) {
1814 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001815 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001816
Felipe Leme637e05e2017-12-06 12:09:37 -08001817 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001818 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001819 return;
1820 }
1821
1822 final int itemCount = ids.size();
1823 int numApplied = 0;
1824 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001825 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1826 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001827
Felipe Leme49f08ed2018-03-26 16:18:45 -07001828 ArrayList<AutofillId> failedIds = null;
1829
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001830 for (int i = 0; i < itemCount; i++) {
1831 final AutofillId id = ids.get(i);
1832 final AutofillValue value = values.get(i);
1833 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001834 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001835 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07001836 // Most likely view has been removed after the initial request was sent to the
1837 // the service; this is fine, but we need to update the view status in the
1838 // server side so it can be triggered again.
1839 Log.d(TAG, "autofill(): no View with id " + id);
1840 if (failedIds == null) {
1841 failedIds = new ArrayList<>();
1842 }
1843 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001844 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001845 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001846 if (id.isVirtual()) {
1847 if (virtualValues == null) {
1848 // Most likely there will be just one view with virtual children.
1849 virtualValues = new ArrayMap<>(1);
1850 }
1851 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1852 if (valuesByParent == null) {
1853 // We don't know the size yet, but usually it will be just a few fields...
1854 valuesByParent = new SparseArray<>(5);
1855 virtualValues.put(view, valuesByParent);
1856 }
1857 valuesByParent.put(id.getVirtualChildId(), value);
1858 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001859 // Mark the view as to be autofilled with 'value'
1860 if (mLastAutofilledData == null) {
1861 mLastAutofilledData = new ParcelableMap(itemCount - i);
1862 }
1863 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001864
1865 view.autofill(value);
1866
1867 // Set as autofilled if the values match now, e.g. when the value was updated
1868 // synchronously.
1869 // If autofill happens async, the view is set to autofilled in
1870 // notifyValueChanged.
1871 setAutofilledIfValuesIs(view, value);
1872
1873 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001874 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001875 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001876
Felipe Leme49f08ed2018-03-26 16:18:45 -07001877 if (failedIds != null) {
1878 if (sVerbose) {
1879 Log.v(TAG, "autofill(): total failed views: " + failedIds);
1880 }
1881 try {
1882 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
1883 } catch (RemoteException e) {
1884 // In theory, we could ignore this error since it's not a big deal, but
1885 // in reality, we rather crash the app anyways, as the failure could be
1886 // a consequence of something going wrong on the server side...
1887 e.rethrowFromSystemServer();
1888 }
1889 }
1890
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001891 if (virtualValues != null) {
1892 for (int i = 0; i < virtualValues.size(); i++) {
1893 final View parent = virtualValues.keyAt(i);
1894 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1895 parent.autofill(childrenValues);
1896 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07001897 // TODO: we should provide a callback so the parent can call failures; something
1898 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001899 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001900 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001901
Felipe Leme11166522018-05-07 10:18:47 -07001902 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07001903 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07001904 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001905 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001906 }
1907
Felipe Leme11166522018-05-07 10:18:47 -07001908 private LogMaker newLog(int category) {
Felipe Leme212b1612018-05-22 14:56:15 -07001909 final LogMaker log = new LogMaker(category)
Felipe Lemece3ae872018-05-24 10:41:48 -07001910 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
1911
1912 if (isCompatibilityModeEnabledLocked()) {
1913 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
1914 }
Felipe Leme212b1612018-05-22 14:56:15 -07001915 final AutofillClient client = getClient();
1916 if (client == null) {
1917 // Client should never be null here, but it doesn't hurt to check...
1918 log.setPackageName(mContext.getPackageName());
1919 } else {
1920 log.setComponentName(client.autofillClientGetComponentName());
1921 }
1922 return log;
Felipe Leme11166522018-05-07 10:18:47 -07001923 }
1924
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001925 /**
1926 * Set the tracked views.
1927 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001928 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001929 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001930 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001931 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001932 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001933 */
Felipe Leme27e20222017-05-18 15:24:11 -07001934 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001935 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1936 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001937 synchronized (mLock) {
1938 if (mEnabled && mSessionId == sessionId) {
1939 if (saveOnAllViewsInvisible) {
1940 mTrackedViews = new TrackedViews(trackedIds);
1941 } else {
1942 mTrackedViews = null;
1943 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001944 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001945 if (fillableIds != null) {
1946 if (mFillableIds == null) {
1947 mFillableIds = new ArraySet<>(fillableIds.length);
1948 }
1949 for (AutofillId id : fillableIds) {
1950 mFillableIds.add(id);
1951 }
1952 if (sVerbose) {
1953 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1954 + ", mFillableIds" + mFillableIds);
1955 }
1956 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001957
1958 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1959 // Turn off trigger on previous view id.
1960 setNotifyOnClickLocked(mSaveTriggerId, false);
1961 }
1962
1963 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1964 // Turn on trigger on new view id.
1965 mSaveTriggerId = saveTriggerId;
1966 setNotifyOnClickLocked(mSaveTriggerId, true);
1967 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001968 }
1969 }
1970 }
1971
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001972 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1973 final View view = findView(id);
1974 if (view == null) {
1975 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1976 return;
1977 }
1978 view.setNotifyAutofillManagerOnClick(notify);
1979 }
1980
Felipe Lemec24a56a2017-08-03 14:27:57 -07001981 private void setSaveUiState(int sessionId, boolean shown) {
1982 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1983 synchronized (mLock) {
1984 if (mSessionId != NO_SESSION) {
1985 // Race condition: app triggered a new session after the previous session was
1986 // finished but before server called setSaveUiState() - need to cancel the new
1987 // session to avoid further inconsistent behavior.
1988 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1989 + ") called on existing session " + mSessionId + "; cancelling it");
1990 cancelSessionLocked();
1991 }
1992 if (shown) {
1993 mSessionId = sessionId;
1994 mState = STATE_SHOWING_SAVE_UI;
1995 } else {
1996 mSessionId = NO_SESSION;
1997 mState = STATE_UNKNOWN;
1998 }
1999 }
2000 }
2001
Felipe Leme650f7ab2017-09-19 13:08:24 -07002002 /**
2003 * Marks the state of the session as finished.
2004 *
2005 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002006 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2007 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
2008 * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
2009 * disabled further autofill requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07002010 */
2011 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002012 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002013 if (sVerbose) {
2014 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
2015 + getStateAsString(newState));
2016 }
2017 if (newState == STATE_UNKNOWN_COMPAT_MODE) {
2018 resetSessionLocked(/* resetEnteredIds= */ true);
2019 mState = STATE_UNKNOWN;
2020 } else {
2021 resetSessionLocked(/* resetEnteredIds= */ false);
2022 mState = newState;
2023 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002024 }
2025 }
2026
Dake Gub0fa3782018-02-26 12:25:14 -08002027 /** @hide */
2028 public void requestHideFillUi() {
2029 requestHideFillUi(mIdShownFillUi, true);
2030 }
2031
2032 private void requestHideFillUi(AutofillId id, boolean force) {
2033 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002034 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002035 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002036 if (force) {
2037 // When user taps outside autofill window, force to close fill ui even id does
2038 // not match.
2039 AutofillClient client = getClient();
2040 if (client != null) {
2041 client.autofillClientRequestHideFillUi();
2042 }
2043 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002044 return;
2045 }
Felipe Leme27e20222017-05-18 15:24:11 -07002046 requestHideFillUi(id, anchor);
2047 }
2048
2049 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002050
2051 AutofillCallback callback = null;
2052 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002053 // We do not check the session id for two reasons:
2054 // 1. If local and remote session id are off sync the UI would be stuck shown
2055 // 2. There is a race between the user state being destroyed due the fill
2056 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002057 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002058 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002059 if (client.autofillClientRequestHideFillUi()) {
2060 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002061 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002062 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002063 }
2064 }
2065
2066 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002067 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002068 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002069 AutofillCallback.EVENT_INPUT_HIDDEN);
2070 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002071 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002072 }
2073 }
2074 }
2075
Felipe Leme17292d12017-10-24 14:03:10 -07002076 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002077 if (sVerbose) {
2078 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002079 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002080 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002081 final View anchor = findView(id);
2082 if (anchor == null) {
2083 return;
2084 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002085
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002086 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002087 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002088 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002089 callback = mCallback;
2090 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002091 }
2092
2093 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002094 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002095 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002096 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2097 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002098 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002099 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002100 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002101
Felipe Leme17292d12017-10-24 14:03:10 -07002102 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002103 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07002104 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07002105 }
2106 }
2107
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002108 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002109 * Find a single view by its id.
2110 *
2111 * @param autofillId The autofill id of the view
2112 *
2113 * @return The view or {@code null} if view was not found
2114 */
2115 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002116 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002117 if (client != null) {
2118 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002119 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002120 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002121 }
2122
Felipe Lemebb810922017-04-25 15:54:06 -07002123 /** @hide */
2124 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002125 return mService != null;
2126 }
2127
Felipe Lemec24a56a2017-08-03 14:27:57 -07002128 /** @hide */
2129 public void onPendingSaveUi(int operation, IBinder token) {
2130 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2131
2132 synchronized (mLock) {
2133 try {
2134 mService.onPendingSaveUi(operation, token);
2135 } catch (RemoteException e) {
2136 e.rethrowFromSystemServer();
2137 }
2138 }
2139 }
2140
2141 /** @hide */
2142 public void dump(String outerPrefix, PrintWriter pw) {
2143 pw.print(outerPrefix); pw.println("AutofillManager:");
2144 final String pfx = outerPrefix + " ";
2145 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002146 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002147 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme42260332018-08-15 08:44:12 -07002148 final AutofillClient client = getClient();
2149 if (client != null) {
2150 pw.print(pfx); pw.print("client: "); pw.print(client);
2151 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2152 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002153 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
2154 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2155 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002156 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002157 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
2158 pw.print(pfx); pw.print("tracked views: ");
2159 if (mTrackedViews == null) {
2160 pw.println("null");
2161 } else {
2162 final String pfx2 = pfx + " ";
2163 pw.println();
2164 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2165 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2166 }
2167 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002168 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002169 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2170 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme42260332018-08-15 08:44:12 -07002171 pw.print(pfx); pw.print("compat mode enabled: ");
2172 synchronized (mLock) {
2173 if (mCompatibilityBridge != null) {
2174 final String pfx2 = pfx + " ";
2175 pw.println("true");
2176 pw.print(pfx2); pw.print("windowId: ");
2177 pw.println(mCompatibilityBridge.mFocusedWindowId);
2178 pw.print(pfx2); pw.print("nodeId: ");
2179 pw.println(mCompatibilityBridge.mFocusedNodeId);
2180 pw.print(pfx2); pw.print("virtualId: ");
2181 pw.println(AccessibilityNodeInfo
2182 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2183 pw.print(pfx2); pw.print("focusedBounds: ");
2184 pw.println(mCompatibilityBridge.mFocusedBounds);
2185 } else {
2186 pw.println("false");
2187 }
2188 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002189 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2190 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002191 }
2192
Andreas Gampe3f24e692018-02-05 13:24:28 -08002193 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002194 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002195 return getStateAsString(mState);
2196 }
2197
2198 @NonNull
2199 private static String getStateAsString(int state) {
2200 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002201 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002202 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002203 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002204 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002205 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002206 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002207 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002208 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002209 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002210 return "DISABLED_BY_SERVICE";
2211 case STATE_UNKNOWN_COMPAT_MODE:
2212 return "UNKNOWN_COMPAT_MODE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002213 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002214 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002215 }
2216 }
2217
Andreas Gampe3f24e692018-02-05 13:24:28 -08002218 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002219 private boolean isActiveLocked() {
2220 return mState == STATE_ACTIVE;
2221 }
2222
Andreas Gampe3f24e692018-02-05 13:24:28 -08002223 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002224 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002225 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002226 }
2227
Andreas Gampe3f24e692018-02-05 13:24:28 -08002228 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002229 private boolean isFinishedLocked() {
2230 return mState == STATE_FINISHED;
2231 }
2232
Felipe Leme9876a6f2017-05-30 15:47:28 -07002233 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002234 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002235 if (client == null) {
2236 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2237 return;
2238 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002239 client.autofillClientRunOnUiThread(runnable);
2240 }
2241
2242 /**
2243 * Implementation of the accessibility based compatibility.
2244 */
2245 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2246 @GuardedBy("mLock")
2247 private final Rect mFocusedBounds = new Rect();
2248 @GuardedBy("mLock")
2249 private final Rect mTempBounds = new Rect();
2250
2251 @GuardedBy("mLock")
2252 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2253 @GuardedBy("mLock")
2254 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2255
2256 // Need to report a fake service in case a11y clients check the service list
2257 @NonNull
2258 @GuardedBy("mLock")
2259 AccessibilityServiceInfo mCompatServiceInfo;
2260
2261 CompatibilityBridge() {
2262 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2263 am.setAccessibilityPolicy(this);
2264 }
2265
2266 private AccessibilityServiceInfo getCompatServiceInfo() {
2267 synchronized (mLock) {
2268 if (mCompatServiceInfo != null) {
2269 return mCompatServiceInfo;
2270 }
2271 final Intent intent = new Intent();
2272 intent.setComponent(new ComponentName("android",
2273 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2274 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2275 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2276 try {
2277 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2278 } catch (XmlPullParserException | IOException e) {
2279 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2280 throw new IllegalStateException("Cannot find compat autofill service");
2281 }
2282 return mCompatServiceInfo;
2283 }
2284 }
2285
2286 @Override
2287 public boolean isEnabled(boolean accessibilityEnabled) {
2288 return true;
2289 }
2290
2291 @Override
2292 public int getRelevantEventTypes(int relevantEventTypes) {
2293 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2294 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002295 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002296 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2297 }
2298
2299 @Override
2300 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2301 List<AccessibilityServiceInfo> installedServices) {
2302 if (installedServices == null) {
2303 installedServices = new ArrayList<>();
2304 }
2305 installedServices.add(getCompatServiceInfo());
2306 return installedServices;
2307 }
2308
2309 @Override
2310 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2311 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2312 if (enabledService == null) {
2313 enabledService = new ArrayList<>();
2314 }
2315 enabledService.add(getCompatServiceInfo());
2316 return enabledService;
2317 }
2318
2319 @Override
2320 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2321 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Lemea9aacf92018-08-13 18:15:32 -07002322 final int type = event.getEventType();
2323 if (sVerbose) {
2324 // NOTE: this is waaay spammy, but that's life.
2325 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme34384212018-08-16 10:49:03 -07002326 + "): virtualId="
2327 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2328 + ", client=" + getClient());
Felipe Lemea9aacf92018-08-13 18:15:32 -07002329 }
2330 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002331 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2332 synchronized (mLock) {
2333 if (mFocusedWindowId == event.getWindowId()
2334 && mFocusedNodeId == event.getSourceNodeId()) {
2335 return event;
2336 }
2337 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2338 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2339 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2340 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2341 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2342 mFocusedBounds.set(0, 0, 0, 0);
2343 }
2344 final int windowId = event.getWindowId();
2345 final long nodeId = event.getSourceNodeId();
2346 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2347 mFocusedWindowId = windowId;
2348 mFocusedNodeId = nodeId;
2349 }
2350 }
2351 } break;
2352
2353 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2354 synchronized (mLock) {
2355 if (mFocusedWindowId == event.getWindowId()
2356 && mFocusedNodeId == event.getSourceNodeId()) {
2357 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2358 }
2359 }
2360 } break;
2361
Felipe Leme54cc6832018-03-06 12:54:31 -08002362 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2363 synchronized (mLock) {
2364 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2365 }
2366 } break;
2367
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002368 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2369 final AutofillClient client = getClient();
2370 if (client != null) {
2371 synchronized (mLock) {
2372 if (client.autofillClientIsFillUiShowing()) {
2373 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2374 }
2375 updateTrackedViewsLocked();
2376 }
2377 }
2378 } break;
2379 }
2380
2381 return accessibilityEnabled ? event : null;
2382 }
2383
2384 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2385 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2386 if (!isVirtualNode(virtualId)) {
2387 return false;
2388 }
2389 final View view = findViewByAccessibilityId(windowId, nodeId);
2390 if (view == null) {
2391 return false;
2392 }
2393 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2394 if (node == null) {
2395 return false;
2396 }
2397 if (!node.isEditable()) {
2398 return false;
2399 }
2400 final Rect newBounds = mTempBounds;
2401 node.getBoundsInScreen(newBounds);
2402 if (newBounds.equals(focusedBounds)) {
2403 return false;
2404 }
2405 focusedBounds.set(newBounds);
2406 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2407 return true;
2408 }
2409
2410 private void notifyViewExited(int windowId, long nodeId) {
2411 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2412 if (!isVirtualNode(virtualId)) {
2413 return;
2414 }
2415 final View view = findViewByAccessibilityId(windowId, nodeId);
2416 if (view == null) {
2417 return;
2418 }
2419 AutofillManager.this.notifyViewExited(view, virtualId);
2420 }
2421
2422 private void notifyValueChanged(int windowId, long nodeId) {
2423 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2424 if (!isVirtualNode(virtualId)) {
2425 return;
2426 }
2427 final View view = findViewByAccessibilityId(windowId, nodeId);
2428 if (view == null) {
2429 return;
2430 }
2431 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2432 if (node == null) {
2433 return;
2434 }
2435 AutofillManager.this.notifyValueChanged(view, virtualId,
2436 AutofillValue.forText(node.getText()));
2437 }
2438
Felipe Leme54cc6832018-03-06 12:54:31 -08002439 private void notifyViewClicked(int windowId, long nodeId) {
2440 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2441 if (!isVirtualNode(virtualId)) {
2442 return;
2443 }
2444 final View view = findViewByAccessibilityId(windowId, nodeId);
2445 if (view == null) {
2446 return;
2447 }
2448 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2449 if (node == null) {
2450 return;
2451 }
2452 AutofillManager.this.notifyViewClicked(view, virtualId);
2453 }
2454
Andreas Gampe3f24e692018-02-05 13:24:28 -08002455 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002456 private void updateTrackedViewsLocked() {
2457 if (mTrackedViews != null) {
2458 mTrackedViews.onVisibleForAutofillChangedLocked();
2459 }
2460 }
2461
2462 private View findViewByAccessibilityId(int windowId, long nodeId) {
2463 final AutofillClient client = getClient();
2464 if (client == null) {
2465 return null;
2466 }
2467 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2468 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2469 }
2470
2471 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2472 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2473 if (provider == null) {
2474 return null;
2475 }
2476 return provider.createAccessibilityNodeInfo(virtualId);
2477 }
2478
2479 private boolean isVirtualNode(int nodeId) {
2480 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2481 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2482 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002483 }
2484
Felipe Lemee6010f22017-03-03 11:19:51 -08002485 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002486 * View tracking information. Once all tracked views become invisible the session is finished.
2487 */
2488 private class TrackedViews {
2489 /** Visible tracked views */
2490 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2491
2492 /** Invisible tracked views */
2493 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2494
2495 /**
2496 * Check if set is null or value is in set.
2497 *
2498 * @param set The set or null (== empty set)
2499 * @param value The value that might be in the set
2500 *
2501 * @return {@code true} iff set is not empty and value is in set
2502 */
Felipe Leme27e20222017-05-18 15:24:11 -07002503 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002504 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2505 return set != null && set.contains(value);
2506 }
2507
2508 /**
2509 * Add a value to a set. If set is null, create a new set.
2510 *
2511 * @param set The set or null (== empty set)
2512 * @param valueToAdd The value to add
2513 *
2514 * @return The set including the new value. If set was {@code null}, a set containing only
2515 * the new value.
2516 */
Felipe Leme27e20222017-05-18 15:24:11 -07002517 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002518 @NonNull
2519 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2520 if (set == null) {
2521 set = new ArraySet<>(1);
2522 }
2523
2524 set.add(valueToAdd);
2525
2526 return set;
2527 }
2528
2529 /**
2530 * Remove a value from a set.
2531 *
2532 * @param set The set or null (== empty set)
2533 * @param valueToRemove The value to remove
2534 *
2535 * @return The set without the removed value. {@code null} if set was null, or is empty
2536 * after removal.
2537 */
Felipe Leme27e20222017-05-18 15:24:11 -07002538 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002539 @Nullable
2540 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2541 if (set == null) {
2542 return null;
2543 }
2544
2545 set.remove(valueToRemove);
2546
2547 if (set.isEmpty()) {
2548 return null;
2549 }
2550
2551 return set;
2552 }
2553
2554 /**
2555 * Set the tracked views.
2556 *
2557 * @param trackedIds The views to be tracked
2558 */
Felipe Leme27e20222017-05-18 15:24:11 -07002559 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002560 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002561 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002562 final boolean[] isVisible;
2563
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002564 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002565 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002566 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002567 } else {
2568 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002569 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002570 }
2571
Felipe Leme27e20222017-05-18 15:24:11 -07002572 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002573 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002574 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002575
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002576 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002577 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002578 } else {
2579 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2580 }
2581 }
2582 }
2583
Felipe Leme9f9ee252017-04-27 13:56:22 -07002584 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002585 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002586 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2587 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2588 }
2589
2590 if (mVisibleTrackedIds == null) {
2591 finishSessionLocked();
2592 }
2593 }
2594
2595 /**
2596 * Called when a {@link View view's} visibility changes.
2597 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002598 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002599 * @param isVisible visible if the view is visible in the view hierarchy.
2600 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002601 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002602 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002603 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002604 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002605 + isVisible);
2606 }
2607
Dake Gu67decfa2017-12-27 11:48:08 -08002608 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002609 if (isVisible) {
2610 if (isInSet(mInvisibleTrackedIds, id)) {
2611 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2612 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2613 }
2614 } else {
2615 if (isInSet(mVisibleTrackedIds, id)) {
2616 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2617 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2618 }
2619 }
2620 }
2621
2622 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002623 if (sVerbose) {
2624 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2625 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002626 finishSessionLocked();
2627 }
2628 }
2629
2630 /**
2631 * Called once the client becomes visible.
2632 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002633 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002634 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002635 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002636 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002637 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002638 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002639 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002640 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2641 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2642 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07002643 if (sVerbose) {
2644 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
2645 + " vis=" + mVisibleTrackedIds);
2646 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002647 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002648 final ArrayList<AutofillId> orderedInvisibleIds =
2649 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002650 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2651 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002652
2653 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2654 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2655 final AutofillId id = orderedInvisibleIds.get(i);
2656 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002657 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2658
Felipe Leme9f9ee252017-04-27 13:56:22 -07002659 if (sDebug) {
2660 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002661 }
2662 } else {
2663 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2664 }
2665 }
2666 }
2667
2668 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002669 final ArrayList<AutofillId> orderedVisibleIds =
2670 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002671 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2672 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002673
2674 final int numVisibleTrackedIds = orderedVisibleIds.size();
2675 for (int i = 0; i < numVisibleTrackedIds; i++) {
2676 final AutofillId id = orderedVisibleIds.get(i);
2677
2678 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002679 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2680 } else {
2681 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2682
Felipe Leme9f9ee252017-04-27 13:56:22 -07002683 if (sDebug) {
2684 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002685 }
2686 }
2687 }
2688 }
2689
2690 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2691 mVisibleTrackedIds = updatedVisibleTrackedIds;
2692 }
2693
2694 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002695 if (sVerbose) {
2696 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
2697 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002698 finishSessionLocked();
2699 }
2700 }
2701 }
2702
2703 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002704 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002705 *
2706 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002707 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002708 */
2709 public abstract static class AutofillCallback {
2710
2711 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002712 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2713 EVENT_INPUT_SHOWN,
2714 EVENT_INPUT_HIDDEN,
2715 EVENT_INPUT_UNAVAILABLE
2716 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002717 @Retention(RetentionPolicy.SOURCE)
2718 public @interface AutofillEventType {}
2719
2720 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002721 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002722 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002723 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002724 * should be hidden upon receiving this event.
2725 */
2726 public static final int EVENT_INPUT_SHOWN = 1;
2727
2728 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002729 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002730 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002731 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002732 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2733 */
2734 public static final int EVENT_INPUT_HIDDEN = 2;
2735
2736 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002737 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002738 * autofill is not available.
2739 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002740 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002741 * to avoid flickering, it could shown it upon receiving this event.
2742 */
2743 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2744
2745 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002746 * Called after a change in the autofill state associated with a view.
2747 *
2748 * @param view view associated with the change.
2749 *
2750 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2751 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002752 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2753 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002754
2755 /**
2756 * Called after a change in the autofill state associated with a virtual view.
2757 *
2758 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002759 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002760 *
2761 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2762 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002763 public void onAutofillEvent(@NonNull View view, int virtualId,
2764 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002765 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002766 }
2767
Felipe Leme640f30a2017-03-06 15:44:06 -08002768 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2769 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002770
Felipe Leme640f30a2017-03-06 15:44:06 -08002771 AutofillManagerClient(AutofillManager autofillManager) {
2772 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002773 }
2774
2775 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002776 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002777 final AutofillManager afm = mAfm.get();
2778 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002779 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002780 }
2781 }
2782
2783 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002784 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002785 final AutofillManager afm = mAfm.get();
2786 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002787 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002788 }
2789 }
2790
2791 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002792 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2793 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002794 final AutofillManager afm = mAfm.get();
2795 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002796 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002797 }
2798 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002799
2800 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002801 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2802 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002803 final AutofillManager afm = mAfm.get();
2804 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002805 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2806 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002807 }
2808 }
2809
2810 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002811 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002812 final AutofillManager afm = mAfm.get();
2813 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002814 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07002815 }
2816 }
2817
2818 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002819 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002820 final AutofillManager afm = mAfm.get();
2821 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002822 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002823 }
2824 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002825
2826 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08002827 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
2828 final AutofillManager afm = mAfm.get();
2829 if (afm != null) {
2830 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
2831 }
2832 }
2833
2834 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002835 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002836 final AutofillManager afm = mAfm.get();
2837 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002838 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002839 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002840 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002841 } catch (IntentSender.SendIntentException e) {
2842 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2843 }
2844 });
2845 }
2846 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002847
2848 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002849 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002850 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2851 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002852 final AutofillManager afm = mAfm.get();
2853 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002854 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2855 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002856 }
2857 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002858
2859 @Override
2860 public void setSaveUiState(int sessionId, boolean shown) {
2861 final AutofillManager afm = mAfm.get();
2862 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002863 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2864 }
2865 }
2866
2867 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002868 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002869 final AutofillManager afm = mAfm.get();
2870 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002871 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002872 }
2873 }
Svet Ganov782043c2017-02-11 00:52:02 +00002874 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002875}