blob: 88300dbda1c448698d915bb9aece20eeff304b51 [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/**
Felipe Leme744976e2017-07-31 11:34:14 -070080 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
81 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070082 *
Felipe Leme744976e2017-07-31 11:34:14 -070083 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
84 * activity context; the autofill context is created when one of the following methods is called for
85 * the first time in an activity context, and the current user has an enabled autofill service:
86 *
87 * <ul>
88 * <li>{@link #notifyViewEntered(View)}
89 * <li>{@link #notifyViewEntered(View, int, Rect)}
90 * <li>{@link #requestAutofill(View)}
91 * </ul>
92 *
93 * <p>Tipically, the context is automatically created when the first view of the activity is
94 * focused because {@code View.onFocusChanged()} indirectly calls
95 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
96 * explicitly create it (for example, a custom view developer could offer a contextual menu action
97 * in a text-field view to let users manually request autofill).
98 *
99 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
100 * that represents the view hierarchy by calling
101 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
102 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
103 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
104 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
105 * the hierarchy.
106 *
107 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
108 * parses it looking for views that can be autofilled. If the service finds such views, it returns
109 * a data structure to the Android System containing the following optional info:
110 *
111 * <ul>
112 * <li>Datasets used to autofill subsets of views in the activity.
113 * <li>Id of views that the service can save their values for future autofilling.
114 * </ul>
115 *
116 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700117 * UI associated with the view, when the view is focused on and is part of a dataset.
118 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700119 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700120 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700121 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
122 *
123 * <p>When the service returns ids of savable views, the Android System keeps track of changes
124 * made to these views, so they can be used to determine if the autofill save UI is shown later.
125 *
126 * <p>The context is then finished when one of the following occurs:
127 *
128 * <ul>
129 * <li>{@link #commit()} is called or all savable views are gone.
130 * <li>{@link #cancel()} is called.
131 * </ul>
132 *
133 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700134 * 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 -0700135 * option to Save, the current value of the views is then sent to the autofill service.
136 *
137 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800138 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600139@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700140@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800141public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800142
Felipe Leme640f30a2017-03-06 15:44:06 -0800143 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800144
Svet Ganov782043c2017-02-11 00:52:02 +0000145 /**
146 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700147 *
Svet Ganov782043c2017-02-11 00:52:02 +0000148 * <p>
149 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000150 */
151 public static final String EXTRA_ASSIST_STRUCTURE =
152 "android.view.autofill.extra.ASSIST_STRUCTURE";
153
154 /**
155 * Intent extra: The result of an authentication operation. It is
156 * either a fully populated {@link android.service.autofill.FillResponse}
157 * or a fully populated {@link android.service.autofill.Dataset} if
158 * a response or a dataset is being authenticated respectively.
159 *
160 * <p>
161 * Type: {@link android.service.autofill.FillResponse} or a
162 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000163 */
164 public static final String EXTRA_AUTHENTICATION_RESULT =
165 "android.view.autofill.extra.AUTHENTICATION_RESULT";
166
Felipe Leme7320ca92017-03-29 15:09:54 -0700167 /**
168 * Intent extra: The optional extras provided by the
169 * {@link android.service.autofill.AutofillService}.
170 *
171 * <p>For example, when the service responds to a {@link
172 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
173 * a {@code FillResponse} that requires authentication, the Intent that launches the
174 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700175 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700176 *
Felipe Lemea9372382017-10-09 14:42:36 -0700177 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
178 * can also add this bundle to the {@link Intent} set as the
179 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
180 * so the bundle can be recovered later on
181 * {@link android.service.autofill.SaveRequest#getClientState()}.
182 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700183 * <p>
184 * Type: {@link android.os.Bundle}
185 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700186 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600187 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700188
Felipe Lemec24a56a2017-08-03 14:27:57 -0700189 /** @hide */
190 public static final String EXTRA_RESTORE_SESSION_TOKEN =
191 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
192
193 private static final String SESSION_ID_TAG = "android:sessionId";
194 private static final String STATE_TAG = "android:state";
195 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
196
Felipe Leme0aa4c502017-04-26 12:36:01 -0700197 /** @hide */ public static final int ACTION_START_SESSION = 1;
198 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
199 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
200 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800201
Felipe Leme9f9ee252017-04-27 13:56:22 -0700202
203 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
204 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
205 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
206
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700207 /** Which bits in an authentication id are used for the dataset id */
208 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
209 /** How many bits in an authentication id are used for the dataset id */
210 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
211 /** @hide The index for an undefined data set */
212 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
213
214 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700215 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
216 *
217 * @hide
218 */
219 public static final int PENDING_UI_OPERATION_CANCEL = 1;
220
221 /**
222 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
223 *
224 * @hide
225 */
226 public static final int PENDING_UI_OPERATION_RESTORE = 2;
227
228 /**
229 * Initial state of the autofill context, set when there is no session (i.e., when
230 * {@link #mSessionId} is {@link #NO_SESSION}).
231 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700232 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
233 * the server.
234 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700235 * @hide
236 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700237 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700238
239 /**
240 * State where the autofill context hasn't been {@link #commit() finished} nor
241 * {@link #cancel() canceled} yet.
242 *
243 * @hide
244 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700245 public static final int STATE_ACTIVE = 1;
246
247 /**
248 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700249 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700250 *
251 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
252 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
253 *
254 * @hide
255 */
256 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700257
258 /**
259 * State where the autofill context has been {@link #commit() finished} but the server still has
260 * a session because the Save UI hasn't been dismissed yet.
261 *
262 * @hide
263 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700264 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700265
266 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700267 * State where the autofill is disabled because the service cannot autofill the activity at all.
268 *
269 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
270 * (and {@link #requestAutofill(View, int, Rect)}).
271 *
272 * @hide
273 */
274 public static final int STATE_DISABLED_BY_SERVICE = 4;
275
276 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700277 * Same as {@link #STATE_UNKNOWN}, but used on
278 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
279 * the URL bar changed on client mode
280 *
281 * @hide
282 */
283 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
284
285
286 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800287 * Timeout in ms for calls to the field classification service.
288 * @hide
289 */
290 public static final int FC_SERVICE_TIMEOUT = 5000;
291
292 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700293 * Makes an authentication id from a request id and a dataset id.
294 *
295 * @param requestId The request id.
296 * @param datasetId The dataset id.
297 * @return The authentication id.
298 * @hide
299 */
300 public static int makeAuthenticationId(int requestId, int datasetId) {
301 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
302 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
303 }
304
305 /**
306 * Gets the request id from an authentication id.
307 *
308 * @param authRequestId The authentication id.
309 * @return The request id.
310 * @hide
311 */
312 public static int getRequestIdFromAuthenticationId(int authRequestId) {
313 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
314 }
315
316 /**
317 * Gets the dataset id from an authentication id.
318 *
319 * @param authRequestId The authentication id.
320 * @return The dataset id.
321 * @hide
322 */
323 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
324 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
325 }
326
Felipe Leme4753bb02017-03-22 20:24:00 -0700327 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000328
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700329 /**
330 * There is currently no session running.
331 * {@hide}
332 */
333 public static final int NO_SESSION = Integer.MIN_VALUE;
334
Svet Ganov782043c2017-02-11 00:52:02 +0000335 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700336
337 private final Object mLock = new Object();
338
339 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000340 private IAutoFillManagerClient mServiceClient;
341
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700342 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900343 private Cleaner mServiceClientCleaner;
344
345 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800346 private AutofillCallback mCallback;
347
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700348 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000349
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700350 @GuardedBy("mLock")
351 private int mSessionId = NO_SESSION;
352
353 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700354 private int mState = STATE_UNKNOWN;
355
356 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000357 private boolean mEnabled;
358
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700359 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700360 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700361 @Nullable private ParcelableMap mLastAutofilledData;
362
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700363 /** If view tracking is enabled, contains the tracking state */
364 @GuardedBy("mLock")
365 @Nullable private TrackedViews mTrackedViews;
366
Felipe Leme27e20222017-05-18 15:24:11 -0700367 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
368 @GuardedBy("mLock")
369 @Nullable private ArraySet<AutofillId> mFillableIds;
370
Dake Gub0fa3782018-02-26 12:25:14 -0800371 /** id of last requested autofill ui */
372 @Nullable private AutofillId mIdShownFillUi;
373
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800374 /**
375 * Views that were already "entered" - if they're entered again when the session is not active,
376 * they're ignored
377 * */
378 @GuardedBy("mLock")
379 @Nullable private ArraySet<AutofillId> mEnteredIds;
380
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700381 /** If set, session is commited when the field is clicked. */
382 @GuardedBy("mLock")
383 @Nullable private AutofillId mSaveTriggerId;
384
Dake Gu67decfa2017-12-27 11:48:08 -0800385 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
386 @GuardedBy("mLock")
387 private boolean mOnInvisibleCalled;
388
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700389 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
390 @GuardedBy("mLock")
391 private boolean mSaveOnFinish;
392
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800393 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
394 @GuardedBy("mLock")
395 private CompatibilityBridge mCompatibilityBridge;
396
Svet Ganov782043c2017-02-11 00:52:02 +0000397 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800398 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000399 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000400 * Asks the client to start an authentication flow.
401 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700402 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000403 * @param intent The authentication intent.
404 * @param fillInIntent The authentication fill-in intent.
405 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800406 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700407 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800408
409 /**
410 * Tells the client this manager has state to be reset.
411 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800412 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700413
414 /**
415 * Request showing the autofill UI.
416 *
417 * @param anchor The real view the UI needs to anchor to.
418 * @param width The width of the fill UI content.
419 * @param height The height of the fill UI content.
420 * @param virtualBounds The bounds of the virtual decendant of the anchor.
421 * @param presenter The presenter that controls the fill UI window.
422 * @return Whether the UI was shown.
423 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800424 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700425 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
426
427 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800428 * Dispatch unhandled keyevent from Autofill window
429 * @param anchor The real view the UI needs to anchor to.
430 * @param keyEvent Unhandled KeyEvent from autofill window.
431 */
432 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
433
434 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700435 * Request hiding the autofill UI.
436 *
437 * @return Whether the UI was hidden.
438 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800439 boolean autofillClientRequestHideFillUi();
440
441 /**
442 * Gets whether the fill UI is currenlty being shown.
443 *
444 * @return Whether the fill UI is currently being shown
445 */
446 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700447
448 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700449 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700450 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700451 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700452 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800453 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700454
455 /**
456 * Checks is the client is currently visible as understood by autofill.
457 *
458 * @return {@code true} if the client is currently visible
459 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800460 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700461
462 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800463 * Client might disable enter/exit event e.g. when activity is paused.
464 */
465 boolean isDisablingEnterExitEventForAutofill();
466
467 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700468 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700469 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800470 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700471 *
Felipe Leme27e20222017-05-18 15:24:11 -0700472 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700473 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800474 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
475 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700476
477 /**
478 * Finds a view by traversing the hierarchies of the client.
479 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800480 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700481 *
482 * @return The view, or {@code null} if not found
483 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800484 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
485
486 /**
487 * Finds a view by a11y id in a given client window.
488 *
489 * @param viewId The accessibility id of the views to find
490 * @param windowId The accessibility window id where to search
491 *
492 * @return The view, or {@code null} if not found
493 */
494 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700495
496 /**
497 * Runs the specified action on the UI thread.
498 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800499 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700500
501 /**
502 * Gets the complete component name of this client.
503 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800504 ComponentName autofillClientGetComponentName();
505
506 /**
507 * Gets the activity token
508 */
509 @Nullable IBinder autofillClientGetActivityToken();
510
511 /**
512 * @return Whether compatibility mode is enabled.
513 */
Felipe Leme42b97932018-02-20 13:04:31 -0800514 boolean autofillClientIsCompatibilityModeEnabled();
515
516 /**
517 * Gets the next unique autofill ID.
518 *
519 * <p>Typically used to manage views whose content is recycled - see
520 * {@link View#setAutofillId(AutofillId)} for more info.
521 *
522 * @return An ID that is unique in the activity.
523 */
524 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000525 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800526
527 /**
528 * @hide
529 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800530 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800531 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800532 mService = service;
533 }
534
535 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800536 * @hide
537 */
538 public void enableCompatibilityMode() {
539 synchronized (mLock) {
540 // The accessibility manager is a singleton so we may need to plug
541 // different bridge based on which activity is currently focused
542 // in the current process. Since compat would be rarely used, just
543 // create and register a new instance every time.
544 mCompatibilityBridge = new CompatibilityBridge();
545 }
546 }
547
548 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700549 * Restore state after activity lifecycle
550 *
551 * @param savedInstanceState The state to be restored
552 *
553 * {@hide}
554 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700555 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700556 if (!hasAutofillFeature()) {
557 return;
558 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700559 synchronized (mLock) {
560 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
561
Felipe Lemec24a56a2017-08-03 14:27:57 -0700562 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700563 Log.w(TAG, "New session was started before onCreate()");
564 return;
565 }
566
567 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700568 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700569
570 if (mSessionId != NO_SESSION) {
571 ensureServiceClientAddedIfNeededLocked();
572
Felipe Leme637e05e2017-12-06 12:09:37 -0800573 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700574 if (client != null) {
575 try {
576 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800577 client.autofillClientGetActivityToken(),
578 mServiceClient.asBinder());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700579
580 if (!sessionWasRestored) {
581 Log.w(TAG, "Session " + mSessionId + " could not be restored");
582 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700583 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700584 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700585 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700586 Log.d(TAG, "session " + mSessionId + " was restored");
587 }
588
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800589 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700590 }
591 } catch (RemoteException e) {
592 Log.e(TAG, "Could not figure out if there was an autofill session", e);
593 }
594 }
595 }
596 }
597 }
598
599 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700600 * Called once the client becomes visible.
601 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800602 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700603 *
604 * {@hide}
605 */
606 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800607 // This gets called when the client just got visible at which point the visibility
608 // of the tracked views may not have been computed (due to a pending layout, etc).
609 // While generally we have no way to know when the UI has settled. We will evaluate
610 // the tracked views state at the end of next frame to guarantee that everything
611 // that may need to be laid out is laid out.
612 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
613 synchronized (mLock) {
614 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
615 mTrackedViews.onVisibleForAutofillChangedLocked();
616 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700617 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800618 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700619 }
620
621 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800622 * Called once the client becomes invisible.
623 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800624 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800625 *
626 * {@hide}
627 */
628 public void onInvisibleForAutofill() {
629 synchronized (mLock) {
630 mOnInvisibleCalled = true;
631 }
632 }
633
634 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700635 * Save state before activity lifecycle
636 *
637 * @param outState Place to store the state
638 *
639 * {@hide}
640 */
641 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700642 if (!hasAutofillFeature()) {
643 return;
644 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700645 synchronized (mLock) {
646 if (mSessionId != NO_SESSION) {
647 outState.putInt(SESSION_ID_TAG, mSessionId);
648 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700649 if (mState != STATE_UNKNOWN) {
650 outState.putInt(STATE_TAG, mState);
651 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700652 if (mLastAutofilledData != null) {
653 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
654 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700655 }
656 }
657
658 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800659 * @hide
660 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800661 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100662 public boolean isCompatibilityModeEnabledLocked() {
663 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800664 }
665
666 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700667 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700668 *
669 * <p>Typically used to determine whether the option to explicitly request autofill should
670 * be offered - see {@link #requestAutofill(View)}.
671 *
672 * @return whether autofill is enabled for the current user.
673 */
674 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800675 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700676 return false;
677 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700678 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800679 if (isDisabledByServiceLocked()) {
680 return false;
681 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700682 ensureServiceClientAddedIfNeededLocked();
683 return mEnabled;
684 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700685 }
686
687 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700688 * Should always be called from {@link AutofillService#getFillEventHistory()}.
689 *
690 * @hide
691 */
692 @Nullable public FillEventHistory getFillEventHistory() {
693 try {
694 return mService.getFillEventHistory();
695 } catch (RemoteException e) {
696 e.rethrowFromSystemServer();
697 return null;
698 }
699 }
700
701 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700702 * Explicitly requests a new autofill context.
703 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700704 * <p>Normally, the autofill context is automatically started if necessary when
705 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
706 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
707 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700708 *
709 * @param view view requesting the new autofill context.
710 */
711 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700712 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700713 }
714
715 /**
716 * Explicitly requests a new autofill context for virtual views.
717 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700718 * <p>Normally, the autofill context is automatically started if necessary when
719 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
720 * cases where it must be explicitly started. For example, when the virtual view offers an
721 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700722 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700723 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
724 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
725 * the absolute bounds could be calculated by:
726 *
727 * <pre class="prettyprint">
728 * int offset[] = new int[2];
729 * getLocationOnScreen(offset);
730 * Rect absBounds = new Rect(bounds.left + offset[0],
731 * bounds.top + offset[1],
732 * bounds.right + offset[0], bounds.bottom + offset[1]);
733 * </pre>
734 *
735 * @param view the virtual view parent.
736 * @param virtualId id identifying the virtual child inside the parent view.
737 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700738 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700739 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
740 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700741 }
742
Felipe Leme2ac463e2017-03-13 14:06:25 -0700743 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700744 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800745 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700746 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800747 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700748 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700749 notifyViewEntered(view, 0);
750 }
751
Andreas Gampe3f24e692018-02-05 13:24:28 -0800752 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800753 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800754 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700755 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800756 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
757 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700758 }
759 return true;
760 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800761 if (isFinishedLocked()) {
762 // Session already finished: ignore if automatic request and view already entered
763 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
764 && mEnteredIds.contains(id)) {
765 if (sVerbose) {
766 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
767 + ") on state " + getStateAsStringLocked()
768 + " because view was already entered: " + mEnteredIds);
769 }
770 return true;
771 }
772 }
773 if (sVerbose) {
774 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
775 + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
Felipe Leme17292d12017-10-24 14:03:10 -0700776 }
777 return false;
778 }
779
Dake Gu67decfa2017-12-27 11:48:08 -0800780 private boolean isClientVisibleForAutofillLocked() {
781 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800782 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800783 }
784
785 private boolean isClientDisablingEnterExitEvent() {
786 final AutofillClient client = getClient();
787 return client != null && client.isDisablingEnterExitEventForAutofill();
788 }
789
Felipe Lemed1146422017-04-26 10:17:05 -0700790 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700791 if (!hasAutofillFeature()) {
792 return;
793 }
Dake Gu67decfa2017-12-27 11:48:08 -0800794 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700795 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800796 callback = notifyViewEnteredLocked(view, flags);
797 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700798
Dake Gu67decfa2017-12-27 11:48:08 -0800799 if (callback != null) {
800 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
801 }
802 }
Svet Ganov782043c2017-02-11 00:52:02 +0000803
Dake Gu67decfa2017-12-27 11:48:08 -0800804 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800805 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800806 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800807 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800808 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800809
810 AutofillCallback callback = null;
811
812 ensureServiceClientAddedIfNeededLocked();
813
814 if (!mEnabled) {
815 if (mCallback != null) {
816 callback = mCallback;
817 }
818 } else {
819 // don't notify entered when Activity is already in background
820 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700821 final AutofillValue value = view.getAutofillValue();
822
Felipe Lemec24a56a2017-08-03 14:27:57 -0700823 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700824 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700825 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700826 } else {
827 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700828 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700829 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800830 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700831 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800832 }
Dake Gu67decfa2017-12-27 11:48:08 -0800833 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800834 }
835
836 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700837 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800838 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700839 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800840 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700841 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700842 if (!hasAutofillFeature()) {
843 return;
844 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700845 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800846 notifyViewExitedLocked(view);
847 }
848 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800849
Andreas Gampe3f24e692018-02-05 13:24:28 -0800850 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800851 void notifyViewExitedLocked(@NonNull View view) {
852 ensureServiceClientAddedIfNeededLocked();
853
854 if (mEnabled && isActiveLocked()) {
855 // dont notify exited when Activity is already in background
856 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -0800857 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800858
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700859 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700860 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700861 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800862 }
863 }
864
865 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700866 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700867 *
868 * @param view {@link View} that was exited.
869 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700870 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700871 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
872 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
873 }
874
875 /**
876 * Called when a virtual view's visibility changed.
877 *
878 * @param view {@link View} that was exited.
879 * @param virtualId id identifying the virtual child inside the parent view.
880 * @param isVisible visible if the view is visible in the view hierarchy.
881 */
882 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
883 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
884 }
885
886 /**
887 * Called when a view/virtual view's visibility changed.
888 *
889 * @param view {@link View} that was exited.
890 * @param virtualId id identifying the virtual child inside the parent view.
891 * @param isVisible visible if the view is visible in the view hierarchy.
892 * @param virtual Whether the view is virtual.
893 */
894 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
895 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700896 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700897 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700898 final AutofillId id = virtual ? getAutofillId(view, virtualId)
899 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -0800900 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700901 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700902 if (mFillableIds.contains(id)) {
903 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
904 requestHideFillUi(id, view);
905 }
906 }
907 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800908 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -0800909 } else if (sVerbose) {
910 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -0700911 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700912 }
913 }
914 }
915
916 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700917 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800918 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700919 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
920 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
921 * the absolute bounds could be calculated by:
922 *
923 * <pre class="prettyprint">
924 * int offset[] = new int[2];
925 * getLocationOnScreen(offset);
926 * Rect absBounds = new Rect(bounds.left + offset[0],
927 * bounds.top + offset[1],
928 * bounds.right + offset[0], bounds.bottom + offset[1]);
929 * </pre>
930 *
931 * @param view the virtual view parent.
932 * @param virtualId id identifying the virtual child inside the parent view.
933 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800934 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700935 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
936 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700937 }
938
Felipe Leme6dcec872017-05-25 11:24:23 -0700939 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700940 if (!hasAutofillFeature()) {
941 return;
942 }
Dake Gu67decfa2017-12-27 11:48:08 -0800943 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700944 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800945 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
946 }
Felipe Leme17292d12017-10-24 14:03:10 -0700947
Dake Gu67decfa2017-12-27 11:48:08 -0800948 if (callback != null) {
949 callback.onAutofillEvent(view, virtualId,
950 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
951 }
952 }
Svet Ganov782043c2017-02-11 00:52:02 +0000953
Dake Gu67decfa2017-12-27 11:48:08 -0800954 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800955 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800956 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
957 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800958 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -0800959 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800960 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -0800961
962 ensureServiceClientAddedIfNeededLocked();
963
964 if (!mEnabled) {
965 if (mCallback != null) {
966 callback = mCallback;
967 }
968 } else {
969 // don't notify entered when Activity is already in background
970 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700971 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700972 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700973 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700974 } else {
975 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700976 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700977 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800978 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700979 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800980 }
Dake Gu67decfa2017-12-27 11:48:08 -0800981 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800982 }
983
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800984 @GuardedBy("mLock")
985 private void addEnteredIdLocked(@NonNull AutofillId id) {
986 if (mEnteredIds == null) {
987 mEnteredIds = new ArraySet<>(1);
988 }
989 mEnteredIds.add(id);
990 }
991
Philip P. Moltmann44611812017-02-23 12:52:46 -0800992 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700993 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800994 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700995 * @param view the virtual view parent.
996 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800997 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700998 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -0800999 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001000 if (!hasAutofillFeature()) {
1001 return;
1002 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001003 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001004 notifyViewExitedLocked(view, virtualId);
1005 }
1006 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001007
Andreas Gampe3f24e692018-02-05 13:24:28 -08001008 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001009 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1010 ensureServiceClientAddedIfNeededLocked();
1011
1012 if (mEnabled && isActiveLocked()) {
1013 // don't notify exited when Activity is already in background
1014 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -07001015 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001016
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001017 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001018 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001019 }
Svet Ganov782043c2017-02-11 00:52:02 +00001020 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001021 }
1022
1023 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001024 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001025 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001026 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001027 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001028 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001029 if (!hasAutofillFeature()) {
1030 return;
1031 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001032 AutofillId id = null;
1033 boolean valueWasRead = false;
1034 AutofillValue value = null;
1035
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001036 synchronized (mLock) {
1037 // If the session is gone some fields might still be highlighted, hence we have to
1038 // remove the isAutofilled property even if no sessions are active.
1039 if (mLastAutofilledData == null) {
1040 view.setAutofilled(false);
1041 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001042 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001043 if (mLastAutofilledData.containsKey(id)) {
1044 value = view.getAutofillValue();
1045 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001046
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001047 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1048 view.setAutofilled(true);
1049 } else {
1050 view.setAutofilled(false);
1051 mLastAutofilledData.remove(id);
1052 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001053 } else {
1054 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001055 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001056 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001057
Felipe Lemec24a56a2017-08-03 14:27:57 -07001058 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001059 if (sVerbose) {
1060 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1061 + "): ignoring on state " + getStateAsStringLocked());
Felipe Lemec7b45292017-09-19 09:06:20 -07001062 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001063 return;
1064 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001065
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001066 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001067 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001068 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001069
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001070 if (!valueWasRead) {
1071 value = view.getAutofillValue();
1072 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001073
Felipe Leme0aa4c502017-04-26 12:36:01 -07001074 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001075 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001076 }
1077
Felipe Lemebab851c2017-02-03 18:45:08 -08001078 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001079 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001080 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001081 * @param view the virtual view parent.
1082 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001083 * @param value new value of the child.
1084 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001085 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001086 if (!hasAutofillFeature()) {
1087 return;
1088 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001089 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001090 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001091 if (sVerbose) {
1092 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1093 + "): ignoring on state " + getStateAsStringLocked());
1094 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001095 return;
1096 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001097
Felipe Leme6dcec872017-05-25 11:24:23 -07001098 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001099 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001100 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001101 }
1102
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001103 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001104 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001105 *
Felipe Leme67e62092018-02-15 14:47:31 -08001106 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001107 */
Felipe Leme67e62092018-02-15 14:47:31 -08001108 public void notifyViewClicked(@NonNull View view) {
1109 notifyViewClicked(view.getAutofillId());
1110 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001111
Felipe Leme67e62092018-02-15 14:47:31 -08001112 /**
1113 * Called to indicate a virtual view has been clicked.
1114 *
1115 * @param view the virtual view parent.
1116 * @param virtualId id identifying the virtual child inside the parent view.
1117 */
1118 public void notifyViewClicked(@NonNull View view, int virtualId) {
1119 notifyViewClicked(getAutofillId(view, virtualId));
1120 }
1121
1122 private void notifyViewClicked(AutofillId id) {
1123 if (!hasAutofillFeature()) {
1124 return;
1125 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001126 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1127
1128 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001129 if (!mEnabled || !isActiveLocked()) {
1130 return;
1131 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001132 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1133 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1134 commitLocked();
1135 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
1136 mContext.getPackageName());
1137 }
1138 }
1139 }
1140
1141 /**
1142 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1143 *
1144 * @hide
1145 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001146 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001147 if (!hasAutofillFeature()) {
1148 return;
1149 }
1150 synchronized (mLock) {
1151 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001152 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001153 commitLocked();
1154 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001155 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001156 cancelLocked();
1157 }
1158 }
1159 }
1160
Felipe Lemebab851c2017-02-03 18:45:08 -08001161 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001162 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001163 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001164 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1165 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1166 * that represent the HTML elements, it should call this method after the form is submitted and
1167 * another page is rendered.
1168 *
1169 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1170 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001171 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001172 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001173 if (!hasAutofillFeature()) {
1174 return;
1175 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001176 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001177 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001178 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001179 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001180 }
1181
Andreas Gampe3f24e692018-02-05 13:24:28 -08001182 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001183 private void commitLocked() {
1184 if (!mEnabled && !isActiveLocked()) {
1185 return;
1186 }
1187 finishSessionLocked();
1188 }
1189
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001190 /**
1191 * Called to indicate the current autofill context should be cancelled.
1192 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001193 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1194 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1195 * that represent the HTML elements, it should call this method if the user does not post the
1196 * form but moves to another form in this page.
1197 *
1198 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1199 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001200 */
1201 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001202 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001203 if (!hasAutofillFeature()) {
1204 return;
1205 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001206 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001207 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001208 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001209 }
1210
Andreas Gampe3f24e692018-02-05 13:24:28 -08001211 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001212 private void cancelLocked() {
1213 if (!mEnabled && !isActiveLocked()) {
1214 return;
1215 }
1216 cancelSessionLocked();
1217 }
1218
Svet Ganovf965b872017-04-28 16:34:02 -07001219 /** @hide */
1220 public void disableOwnedAutofillServices() {
1221 disableAutofillServices();
1222 }
1223
Svet Ganovf20a0372017-04-10 17:08:05 -07001224 /**
1225 * If the app calling this API has enabled autofill services they
1226 * will be disabled.
1227 */
Svet Ganovf965b872017-04-28 16:34:02 -07001228 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001229 if (!hasAutofillFeature()) {
1230 return;
1231 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001232 try {
1233 mService.disableOwnedAutofillServices(mContext.getUserId());
1234 } catch (RemoteException e) {
1235 throw e.rethrowFromSystemServer();
1236 }
1237 }
1238
Felipe Lemedb041182017-04-21 17:33:38 -07001239 /**
1240 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1241 * enabled for the current user, or {@code false} otherwise.
1242 */
1243 public boolean hasEnabledAutofillServices() {
1244 if (mService == null) return false;
1245
1246 try {
1247 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1248 } catch (RemoteException e) {
1249 throw e.rethrowFromSystemServer();
1250 }
1251 }
1252
1253 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001254 * Returns the component name of the {@link AutofillService} that is enabled for the current
1255 * user.
1256 */
1257 @Nullable
1258 public ComponentName getAutofillServiceComponentName() {
1259 if (mService == null) return null;
1260
1261 try {
1262 return mService.getAutofillServiceComponentName();
1263 } catch (RemoteException e) {
1264 throw e.rethrowFromSystemServer();
1265 }
1266 }
1267
1268 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001269 * Gets the id of the {@link UserData} used for
1270 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1271 *
1272 * <p>This method is useful when the service must check the status of the {@link UserData} in
1273 * the device without fetching the whole object.
1274 *
1275 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1276 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1277 * the user.
1278 *
1279 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1280 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1281 * service for the user.
1282 */
1283 @Nullable public String getUserDataId() {
1284 try {
1285 return mService.getUserDataId();
1286 } catch (RemoteException e) {
1287 e.rethrowFromSystemServer();
1288 return null;
1289 }
1290 }
1291
1292 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001293 * Gets the user data used for
1294 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001295 *
Felipe Leme27f45732017-12-22 09:05:22 -08001296 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1297 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1298 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001299 *
Felipe Leme452886a2017-11-27 13:09:13 -08001300 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1301 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001302 */
Felipe Leme452886a2017-11-27 13:09:13 -08001303 @Nullable public UserData getUserData() {
1304 try {
1305 return mService.getUserData();
1306 } catch (RemoteException e) {
1307 e.rethrowFromSystemServer();
1308 return null;
1309 }
1310 }
1311
1312 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001313 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001314 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001315 *
1316 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1317 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1318 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001319 */
Felipe Leme452886a2017-11-27 13:09:13 -08001320 public void setUserData(@Nullable UserData userData) {
1321 try {
1322 mService.setUserData(userData);
1323 } catch (RemoteException e) {
1324 e.rethrowFromSystemServer();
1325 }
1326 }
1327
1328 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001329 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1330 * enabled.
1331 *
1332 * <p>As field classification is an expensive operation, it could be disabled, either
1333 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1334 * permanently (for example, because the device is a low-level device).
1335 *
1336 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1337 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1338 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001339 */
Felipe Leme329d0402017-12-06 09:22:43 -08001340 public boolean isFieldClassificationEnabled() {
1341 try {
1342 return mService.isFieldClassificationEnabled();
1343 } catch (RemoteException e) {
1344 e.rethrowFromSystemServer();
1345 return false;
1346 }
1347 }
1348
1349 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001350 * Gets the name of the default algorithm used for
1351 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1352 *
1353 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1354 * set.
1355 *
1356 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1357 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1358 * the user.
1359 */
1360 @Nullable
1361 public String getDefaultFieldClassificationAlgorithm() {
1362 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001363 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001364 } catch (RemoteException e) {
1365 e.rethrowFromSystemServer();
1366 return null;
1367 }
1368 }
1369
1370 /**
1371 * Gets the name of all algorithms currently available for
1372 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1373 *
1374 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001375 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1376 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001377 */
1378 @NonNull
1379 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001380 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001381 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001382 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1383 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001384 } catch (RemoteException e) {
1385 e.rethrowFromSystemServer();
1386 return null;
1387 }
1388 }
1389
1390 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001391 * Returns {@code true} if autofill is supported by the current device and
1392 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001393 *
1394 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1395 * <ol>
1396 * <li>Low-end devices.
1397 * <li>Device policy rules that forbid its usage.
1398 * </ol>
1399 */
1400 public boolean isAutofillSupported() {
1401 if (mService == null) return false;
1402
1403 try {
1404 return mService.isServiceSupported(mContext.getUserId());
1405 } catch (RemoteException e) {
1406 throw e.rethrowFromSystemServer();
1407 }
1408 }
1409
Felipe Leme637e05e2017-12-06 12:09:37 -08001410 // Note: don't need to use locked suffix because mContext is final.
1411 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001412 final AutofillClient client = mContext.getAutofillClient();
1413 if (client == null && sDebug) {
1414 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1415 + mContext);
1416 }
1417 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001418 }
1419
Dake Gud9dbd272018-03-13 11:38:42 -07001420 /**
1421 * Check if autofill ui is showing, must be called on UI thread.
1422 * @hide
1423 */
1424 public boolean isAutofillUiShowing() {
1425 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001426 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001427 }
1428
Svet Ganov782043c2017-02-11 00:52:02 +00001429 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001430 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001431 if (!hasAutofillFeature()) {
1432 return;
1433 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001434 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001435 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1436 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1437 // service set the extra and returned RESULT_CANCELED...
1438
Felipe Leme9f9ee252017-04-27 13:56:22 -07001439 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001440
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001441 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001442 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001443 return;
1444 }
Dake Gu67decfa2017-12-27 11:48:08 -08001445 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1446 // of app activity. We enforce enter event to re-show fill ui in such case.
1447 // CTS example:
1448 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1449 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1450 if (!mOnInvisibleCalled && focusView != null
1451 && focusView.canNotifyAutofillEnterExitEvent()) {
1452 notifyViewExitedLocked(focusView);
1453 notifyViewEnteredLocked(focusView, 0);
1454 }
1455 if (data == null) {
1456 // data is set to null when result is not RESULT_OK
1457 return;
1458 }
1459
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001460 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1461 final Bundle responseData = new Bundle();
1462 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001463 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1464 if (newClientState != null) {
1465 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1466 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001467 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001468 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1469 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001470 } catch (RemoteException e) {
1471 Log.e(TAG, "Error delivering authentication result", e);
1472 }
Svet Ganov782043c2017-02-11 00:52:02 +00001473 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001474 }
1475
Felipe Leme42b97932018-02-20 13:04:31 -08001476 /**
1477 * Gets the next unique autofill ID for the activity context.
1478 *
1479 * <p>Typically used to manage views whose content is recycled - see
1480 * {@link View#setAutofillId(AutofillId)} for more info.
1481 *
1482 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1483 * the {@link Context} associated with this {@link AutofillManager}.
1484 */
1485 @Nullable
1486 public AutofillId getNextAutofillId() {
1487 final AutofillClient client = getClient();
1488 if (client == null) return null;
1489
1490 final AutofillId id = client.autofillClientGetNextAutofillId();
1491
1492 if (id == null && sDebug) {
1493 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1494 }
1495
1496 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001497 }
1498
Felipe Leme6dcec872017-05-25 11:24:23 -07001499 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001500 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001501 }
1502
Andreas Gampe3f24e692018-02-05 13:24:28 -08001503 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001504 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1505 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001506 if (sVerbose) {
1507 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001508 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001509 + ", compatMode=" + isCompatibilityModeEnabledLocked()
1510 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001511 }
Felipe Leme3103c632017-12-18 15:05:14 -08001512 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001513 if (sVerbose) {
1514 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001515 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001516 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001517 return;
1518 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001519 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001520 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001521 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001522
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001523 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001524 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme185de722018-02-13 17:25:44 -08001525 mCallback != null, flags, client.autofillClientGetComponentName(),
1526 isCompatibilityModeEnabledLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001527 if (mSessionId != NO_SESSION) {
1528 mState = STATE_ACTIVE;
1529 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001530 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001531 } catch (RemoteException e) {
1532 throw e.rethrowFromSystemServer();
1533 }
1534 }
1535
Andreas Gampe3f24e692018-02-05 13:24:28 -08001536 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001537 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001538 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001539
1540 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001541
Svet Ganov782043c2017-02-11 00:52:02 +00001542 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001543 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001544 } catch (RemoteException e) {
1545 throw e.rethrowFromSystemServer();
1546 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001547
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001548 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001549 }
1550
Andreas Gampe3f24e692018-02-05 13:24:28 -08001551 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001552 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001553 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001554
1555 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001556
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001557 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001558 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001559 } catch (RemoteException e) {
1560 throw e.rethrowFromSystemServer();
1561 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001562
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001563 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001564 }
1565
Andreas Gampe3f24e692018-02-05 13:24:28 -08001566 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001567 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001568 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001569 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001570 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001571 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001572 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001573 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001574 if (resetEnteredIds) {
1575 mEnteredIds = null;
1576 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001577 }
1578
Andreas Gampe3f24e692018-02-05 13:24:28 -08001579 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001580 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1581 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001582 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001583 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1584 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001585 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001586 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1587
Felipe Leme3461d3c2017-01-19 08:54:55 -08001588 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001589 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001590 final AutofillClient client = getClient();
1591 if (client == null) return; // NOTE: getClient() already logd it..
1592
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001593 final int newId = mService.updateOrRestartSession(
1594 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001595 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001596 mCallback != null, flags, client.autofillClientGetComponentName(),
Felipe Leme185de722018-02-13 17:25:44 -08001597 mSessionId, action, isCompatibilityModeEnabledLocked());
Felipe Leme7f33cd32017-05-11 10:10:49 -07001598 if (newId != mSessionId) {
1599 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1600 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001601 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001602 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001603 }
1604 } else {
1605 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1606 mContext.getUserId());
1607 }
1608
Felipe Leme3461d3c2017-01-19 08:54:55 -08001609 } catch (RemoteException e) {
1610 throw e.rethrowFromSystemServer();
1611 }
1612 }
Svet Ganov782043c2017-02-11 00:52:02 +00001613
Andreas Gampe3f24e692018-02-05 13:24:28 -08001614 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001615 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001616 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001617 return;
1618 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001619
Svet Ganov782043c2017-02-11 00:52:02 +00001620 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001621 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001622 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001623 final int userId = mContext.getUserId();
1624 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001625 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1626 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1627 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001628 final IAutoFillManager service = mService;
1629 final IAutoFillManagerClient serviceClient = mServiceClient;
1630 mServiceClientCleaner = Cleaner.create(this, () -> {
1631 try {
1632 service.removeClient(serviceClient, userId);
1633 } catch (RemoteException e) {
1634 }
1635 });
Svet Ganov782043c2017-02-11 00:52:02 +00001636 } catch (RemoteException e) {
1637 throw e.rethrowFromSystemServer();
1638 }
1639 }
1640 }
1641
Felipe Lemee6010f22017-03-03 11:19:51 -08001642 /**
1643 * Registers a {@link AutofillCallback} to receive autofill events.
1644 *
1645 * @param callback callback to receive events.
1646 */
1647 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001648 if (!hasAutofillFeature()) {
1649 return;
1650 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001651 synchronized (mLock) {
1652 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001653
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001654 final boolean hadCallback = mCallback != null;
1655 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001656
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001657 if (!hadCallback) {
1658 try {
1659 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1660 } catch (RemoteException e) {
1661 throw e.rethrowFromSystemServer();
1662 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001663 }
1664 }
1665 }
1666
1667 /**
1668 * Unregisters a {@link AutofillCallback} to receive autofill events.
1669 *
1670 * @param callback callback to stop receiving events.
1671 */
1672 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001673 if (!hasAutofillFeature()) {
1674 return;
1675 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001676 synchronized (mLock) {
1677 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001678
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001679 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001680
Felipe Lemee6010f22017-03-03 11:19:51 -08001681 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001682 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001683 } catch (RemoteException e) {
1684 throw e.rethrowFromSystemServer();
1685 }
1686 }
1687 }
1688
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001689 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1690 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1691 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001692 if (anchor == null) {
1693 return;
1694 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001695
1696 AutofillCallback callback = null;
1697 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001698 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001699 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001700
1701 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001702 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08001703 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001704 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08001705 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001706 }
1707 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001708 }
1709 }
1710
1711 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001712 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001713 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001714 AutofillCallback.EVENT_INPUT_SHOWN);
1715 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001716 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001717 }
1718 }
1719 }
1720
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001721 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1722 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001723 synchronized (mLock) {
1724 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001725 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001726 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001727 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1728 // before onAuthenticationResult()
1729 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001730 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001731 }
1732 }
1733 }
1734 }
1735
Dake Gu6a20a192018-02-08 12:09:30 -08001736 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
1737 final View anchor = findView(id);
1738 if (anchor == null) {
1739 return;
1740 }
1741
1742 AutofillCallback callback = null;
1743 synchronized (mLock) {
1744 if (mSessionId == sessionId) {
1745 AutofillClient client = getClient();
1746
1747 if (client != null) {
1748 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
1749 }
1750 }
1751 }
1752 }
1753
Felipe Leme51e29da2017-10-24 14:03:10 -07001754 /** @hide */
1755 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1756 /** @hide */
1757 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1758 /** @hide */
1759 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1760 /** @hide */
1761 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1762 /** @hide */
1763 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1764
1765 private void setState(int flags) {
1766 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001767 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001768 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1769 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001770 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001771 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001772 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001773 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001774 // Reset connection to system
1775 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001776 if (mServiceClientCleaner != null) {
1777 mServiceClientCleaner.clean();
1778 mServiceClientCleaner = null;
1779 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001780 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001781 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001782 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1783 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001784 }
1785
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001786 /**
1787 * Sets a view as autofilled if the current value is the {code targetValue}.
1788 *
1789 * @param view The view that is to be autofilled
1790 * @param targetValue The value we want to fill into view
1791 */
1792 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1793 AutofillValue currentValue = view.getAutofillValue();
1794 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001795 synchronized (mLock) {
1796 if (mLastAutofilledData == null) {
1797 mLastAutofilledData = new ParcelableMap(1);
1798 }
Felipe Leme42b97932018-02-20 13:04:31 -08001799 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001800 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001801 view.setAutofilled(true);
1802 }
1803 }
1804
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001805 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001806 synchronized (mLock) {
1807 if (sessionId != mSessionId) {
1808 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001809 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001810
Felipe Leme637e05e2017-12-06 12:09:37 -08001811 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001812 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001813 return;
1814 }
1815
1816 final int itemCount = ids.size();
1817 int numApplied = 0;
1818 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001819 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1820 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001821
Felipe Leme49f08ed2018-03-26 16:18:45 -07001822 ArrayList<AutofillId> failedIds = null;
1823
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001824 for (int i = 0; i < itemCount; i++) {
1825 final AutofillId id = ids.get(i);
1826 final AutofillValue value = values.get(i);
1827 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001828 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001829 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07001830 // Most likely view has been removed after the initial request was sent to the
1831 // the service; this is fine, but we need to update the view status in the
1832 // server side so it can be triggered again.
1833 Log.d(TAG, "autofill(): no View with id " + id);
1834 if (failedIds == null) {
1835 failedIds = new ArrayList<>();
1836 }
1837 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001838 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001839 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001840 if (id.isVirtual()) {
1841 if (virtualValues == null) {
1842 // Most likely there will be just one view with virtual children.
1843 virtualValues = new ArrayMap<>(1);
1844 }
1845 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1846 if (valuesByParent == null) {
1847 // We don't know the size yet, but usually it will be just a few fields...
1848 valuesByParent = new SparseArray<>(5);
1849 virtualValues.put(view, valuesByParent);
1850 }
1851 valuesByParent.put(id.getVirtualChildId(), value);
1852 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001853 // Mark the view as to be autofilled with 'value'
1854 if (mLastAutofilledData == null) {
1855 mLastAutofilledData = new ParcelableMap(itemCount - i);
1856 }
1857 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001858
1859 view.autofill(value);
1860
1861 // Set as autofilled if the values match now, e.g. when the value was updated
1862 // synchronously.
1863 // If autofill happens async, the view is set to autofilled in
1864 // notifyValueChanged.
1865 setAutofilledIfValuesIs(view, value);
1866
1867 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001868 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001869 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001870
Felipe Leme49f08ed2018-03-26 16:18:45 -07001871 if (failedIds != null) {
1872 if (sVerbose) {
1873 Log.v(TAG, "autofill(): total failed views: " + failedIds);
1874 }
1875 try {
1876 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
1877 } catch (RemoteException e) {
1878 // In theory, we could ignore this error since it's not a big deal, but
1879 // in reality, we rather crash the app anyways, as the failure could be
1880 // a consequence of something going wrong on the server side...
1881 e.rethrowFromSystemServer();
1882 }
1883 }
1884
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001885 if (virtualValues != null) {
1886 for (int i = 0; i < virtualValues.size(); i++) {
1887 final View parent = virtualValues.keyAt(i);
1888 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1889 parent.autofill(childrenValues);
1890 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07001891 // TODO: we should provide a callback so the parent can call failures; something
1892 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001893 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001894 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001895
Felipe Lemeb22d6352017-09-08 20:03:53 -07001896 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1897 .setPackageName(mContext.getPackageName())
1898 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1899 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001900 mMetricsLogger.write(log);
1901 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001902 }
1903
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001904 /**
1905 * Set the tracked views.
1906 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001907 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001908 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001909 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001910 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001911 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001912 */
Felipe Leme27e20222017-05-18 15:24:11 -07001913 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001914 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1915 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001916 synchronized (mLock) {
1917 if (mEnabled && mSessionId == sessionId) {
1918 if (saveOnAllViewsInvisible) {
1919 mTrackedViews = new TrackedViews(trackedIds);
1920 } else {
1921 mTrackedViews = null;
1922 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001923 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001924 if (fillableIds != null) {
1925 if (mFillableIds == null) {
1926 mFillableIds = new ArraySet<>(fillableIds.length);
1927 }
1928 for (AutofillId id : fillableIds) {
1929 mFillableIds.add(id);
1930 }
1931 if (sVerbose) {
1932 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1933 + ", mFillableIds" + mFillableIds);
1934 }
1935 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001936
1937 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1938 // Turn off trigger on previous view id.
1939 setNotifyOnClickLocked(mSaveTriggerId, false);
1940 }
1941
1942 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1943 // Turn on trigger on new view id.
1944 mSaveTriggerId = saveTriggerId;
1945 setNotifyOnClickLocked(mSaveTriggerId, true);
1946 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001947 }
1948 }
1949 }
1950
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001951 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1952 final View view = findView(id);
1953 if (view == null) {
1954 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1955 return;
1956 }
1957 view.setNotifyAutofillManagerOnClick(notify);
1958 }
1959
Felipe Lemec24a56a2017-08-03 14:27:57 -07001960 private void setSaveUiState(int sessionId, boolean shown) {
1961 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1962 synchronized (mLock) {
1963 if (mSessionId != NO_SESSION) {
1964 // Race condition: app triggered a new session after the previous session was
1965 // finished but before server called setSaveUiState() - need to cancel the new
1966 // session to avoid further inconsistent behavior.
1967 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1968 + ") called on existing session " + mSessionId + "; cancelling it");
1969 cancelSessionLocked();
1970 }
1971 if (shown) {
1972 mSessionId = sessionId;
1973 mState = STATE_SHOWING_SAVE_UI;
1974 } else {
1975 mSessionId = NO_SESSION;
1976 mState = STATE_UNKNOWN;
1977 }
1978 }
1979 }
1980
Felipe Leme650f7ab2017-09-19 13:08:24 -07001981 /**
1982 * Marks the state of the session as finished.
1983 *
1984 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07001985 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
1986 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
1987 * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
1988 * disabled further autofill requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001989 */
1990 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001991 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07001992 if (sVerbose) {
1993 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
1994 + getStateAsString(newState));
1995 }
1996 if (newState == STATE_UNKNOWN_COMPAT_MODE) {
1997 resetSessionLocked(/* resetEnteredIds= */ true);
1998 mState = STATE_UNKNOWN;
1999 } else {
2000 resetSessionLocked(/* resetEnteredIds= */ false);
2001 mState = newState;
2002 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002003 }
2004 }
2005
Dake Gub0fa3782018-02-26 12:25:14 -08002006 /** @hide */
2007 public void requestHideFillUi() {
2008 requestHideFillUi(mIdShownFillUi, true);
2009 }
2010
2011 private void requestHideFillUi(AutofillId id, boolean force) {
2012 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002013 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002014 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002015 if (force) {
2016 // When user taps outside autofill window, force to close fill ui even id does
2017 // not match.
2018 AutofillClient client = getClient();
2019 if (client != null) {
2020 client.autofillClientRequestHideFillUi();
2021 }
2022 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002023 return;
2024 }
Felipe Leme27e20222017-05-18 15:24:11 -07002025 requestHideFillUi(id, anchor);
2026 }
2027
2028 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002029
2030 AutofillCallback callback = null;
2031 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002032 // We do not check the session id for two reasons:
2033 // 1. If local and remote session id are off sync the UI would be stuck shown
2034 // 2. There is a race between the user state being destroyed due the fill
2035 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002036 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002037 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002038 if (client.autofillClientRequestHideFillUi()) {
2039 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002040 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002041 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002042 }
2043 }
2044
2045 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002046 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002047 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002048 AutofillCallback.EVENT_INPUT_HIDDEN);
2049 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002050 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002051 }
2052 }
2053 }
2054
Felipe Leme17292d12017-10-24 14:03:10 -07002055 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002056 if (sVerbose) {
2057 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002058 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002059 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002060 final View anchor = findView(id);
2061 if (anchor == null) {
2062 return;
2063 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002064
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002065 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002066 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002067 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002068 callback = mCallback;
2069 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002070 }
2071
2072 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002073 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002074 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002075 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2076 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002077 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002078 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002079 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002080
Felipe Leme17292d12017-10-24 14:03:10 -07002081 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002082 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07002083 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07002084 }
2085 }
2086
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002087 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002088 * Find a single view by its id.
2089 *
2090 * @param autofillId The autofill id of the view
2091 *
2092 * @return The view or {@code null} if view was not found
2093 */
2094 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002095 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002096 if (client != null) {
2097 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002098 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002099 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002100 }
2101
Felipe Lemebb810922017-04-25 15:54:06 -07002102 /** @hide */
2103 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002104 return mService != null;
2105 }
2106
Felipe Lemec24a56a2017-08-03 14:27:57 -07002107 /** @hide */
2108 public void onPendingSaveUi(int operation, IBinder token) {
2109 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2110
2111 synchronized (mLock) {
2112 try {
2113 mService.onPendingSaveUi(operation, token);
2114 } catch (RemoteException e) {
2115 e.rethrowFromSystemServer();
2116 }
2117 }
2118 }
2119
2120 /** @hide */
2121 public void dump(String outerPrefix, PrintWriter pw) {
2122 pw.print(outerPrefix); pw.println("AutofillManager:");
2123 final String pfx = outerPrefix + " ";
2124 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002125 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002126 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08002127 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07002128 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
2129 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2130 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002131 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002132 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
2133 pw.print(pfx); pw.print("tracked views: ");
2134 if (mTrackedViews == null) {
2135 pw.println("null");
2136 } else {
2137 final String pfx2 = pfx + " ";
2138 pw.println();
2139 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2140 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2141 }
2142 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002143 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002144 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2145 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
felipealfd3f8f62018-02-07 11:49:07 +01002146 pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
2147 isCompatibilityModeEnabledLocked());
Felipe Leme51e29da2017-10-24 14:03:10 -07002148 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2149 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002150 }
2151
Andreas Gampe3f24e692018-02-05 13:24:28 -08002152 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002153 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002154 return getStateAsString(mState);
2155 }
2156
2157 @NonNull
2158 private static String getStateAsString(int state) {
2159 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002160 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002161 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002162 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002163 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002164 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002165 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002166 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002167 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002168 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002169 return "DISABLED_BY_SERVICE";
2170 case STATE_UNKNOWN_COMPAT_MODE:
2171 return "UNKNOWN_COMPAT_MODE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002172 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002173 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002174 }
2175 }
2176
Andreas Gampe3f24e692018-02-05 13:24:28 -08002177 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002178 private boolean isActiveLocked() {
2179 return mState == STATE_ACTIVE;
2180 }
2181
Andreas Gampe3f24e692018-02-05 13:24:28 -08002182 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002183 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002184 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002185 }
2186
Andreas Gampe3f24e692018-02-05 13:24:28 -08002187 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002188 private boolean isFinishedLocked() {
2189 return mState == STATE_FINISHED;
2190 }
2191
Felipe Leme9876a6f2017-05-30 15:47:28 -07002192 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002193 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002194 if (client == null) {
2195 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2196 return;
2197 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002198 client.autofillClientRunOnUiThread(runnable);
2199 }
2200
2201 /**
2202 * Implementation of the accessibility based compatibility.
2203 */
2204 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2205 @GuardedBy("mLock")
2206 private final Rect mFocusedBounds = new Rect();
2207 @GuardedBy("mLock")
2208 private final Rect mTempBounds = new Rect();
2209
2210 @GuardedBy("mLock")
2211 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2212 @GuardedBy("mLock")
2213 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2214
2215 // Need to report a fake service in case a11y clients check the service list
2216 @NonNull
2217 @GuardedBy("mLock")
2218 AccessibilityServiceInfo mCompatServiceInfo;
2219
2220 CompatibilityBridge() {
2221 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2222 am.setAccessibilityPolicy(this);
2223 }
2224
2225 private AccessibilityServiceInfo getCompatServiceInfo() {
2226 synchronized (mLock) {
2227 if (mCompatServiceInfo != null) {
2228 return mCompatServiceInfo;
2229 }
2230 final Intent intent = new Intent();
2231 intent.setComponent(new ComponentName("android",
2232 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2233 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2234 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2235 try {
2236 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2237 } catch (XmlPullParserException | IOException e) {
2238 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2239 throw new IllegalStateException("Cannot find compat autofill service");
2240 }
2241 return mCompatServiceInfo;
2242 }
2243 }
2244
2245 @Override
2246 public boolean isEnabled(boolean accessibilityEnabled) {
2247 return true;
2248 }
2249
2250 @Override
2251 public int getRelevantEventTypes(int relevantEventTypes) {
2252 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2253 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002254 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002255 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2256 }
2257
2258 @Override
2259 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2260 List<AccessibilityServiceInfo> installedServices) {
2261 if (installedServices == null) {
2262 installedServices = new ArrayList<>();
2263 }
2264 installedServices.add(getCompatServiceInfo());
2265 return installedServices;
2266 }
2267
2268 @Override
2269 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2270 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2271 if (enabledService == null) {
2272 enabledService = new ArrayList<>();
2273 }
2274 enabledService.add(getCompatServiceInfo());
2275 return enabledService;
2276 }
2277
2278 @Override
2279 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2280 boolean accessibilityEnabled, int relevantEventTypes) {
2281 switch (event.getEventType()) {
2282 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2283 synchronized (mLock) {
2284 if (mFocusedWindowId == event.getWindowId()
2285 && mFocusedNodeId == event.getSourceNodeId()) {
2286 return event;
2287 }
2288 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2289 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2290 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2291 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2292 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2293 mFocusedBounds.set(0, 0, 0, 0);
2294 }
2295 final int windowId = event.getWindowId();
2296 final long nodeId = event.getSourceNodeId();
2297 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2298 mFocusedWindowId = windowId;
2299 mFocusedNodeId = nodeId;
2300 }
2301 }
2302 } break;
2303
2304 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2305 synchronized (mLock) {
2306 if (mFocusedWindowId == event.getWindowId()
2307 && mFocusedNodeId == event.getSourceNodeId()) {
2308 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2309 }
2310 }
2311 } break;
2312
Felipe Leme54cc6832018-03-06 12:54:31 -08002313 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2314 synchronized (mLock) {
2315 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2316 }
2317 } break;
2318
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002319 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2320 final AutofillClient client = getClient();
2321 if (client != null) {
2322 synchronized (mLock) {
2323 if (client.autofillClientIsFillUiShowing()) {
2324 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2325 }
2326 updateTrackedViewsLocked();
2327 }
2328 }
2329 } break;
2330 }
2331
2332 return accessibilityEnabled ? event : null;
2333 }
2334
2335 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2336 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2337 if (!isVirtualNode(virtualId)) {
2338 return false;
2339 }
2340 final View view = findViewByAccessibilityId(windowId, nodeId);
2341 if (view == null) {
2342 return false;
2343 }
2344 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2345 if (node == null) {
2346 return false;
2347 }
2348 if (!node.isEditable()) {
2349 return false;
2350 }
2351 final Rect newBounds = mTempBounds;
2352 node.getBoundsInScreen(newBounds);
2353 if (newBounds.equals(focusedBounds)) {
2354 return false;
2355 }
2356 focusedBounds.set(newBounds);
2357 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2358 return true;
2359 }
2360
2361 private void notifyViewExited(int windowId, long nodeId) {
2362 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2363 if (!isVirtualNode(virtualId)) {
2364 return;
2365 }
2366 final View view = findViewByAccessibilityId(windowId, nodeId);
2367 if (view == null) {
2368 return;
2369 }
2370 AutofillManager.this.notifyViewExited(view, virtualId);
2371 }
2372
2373 private void notifyValueChanged(int windowId, long nodeId) {
2374 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2375 if (!isVirtualNode(virtualId)) {
2376 return;
2377 }
2378 final View view = findViewByAccessibilityId(windowId, nodeId);
2379 if (view == null) {
2380 return;
2381 }
2382 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2383 if (node == null) {
2384 return;
2385 }
2386 AutofillManager.this.notifyValueChanged(view, virtualId,
2387 AutofillValue.forText(node.getText()));
2388 }
2389
Felipe Leme54cc6832018-03-06 12:54:31 -08002390 private void notifyViewClicked(int windowId, long nodeId) {
2391 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2392 if (!isVirtualNode(virtualId)) {
2393 return;
2394 }
2395 final View view = findViewByAccessibilityId(windowId, nodeId);
2396 if (view == null) {
2397 return;
2398 }
2399 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2400 if (node == null) {
2401 return;
2402 }
2403 AutofillManager.this.notifyViewClicked(view, virtualId);
2404 }
2405
Andreas Gampe3f24e692018-02-05 13:24:28 -08002406 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002407 private void updateTrackedViewsLocked() {
2408 if (mTrackedViews != null) {
2409 mTrackedViews.onVisibleForAutofillChangedLocked();
2410 }
2411 }
2412
2413 private View findViewByAccessibilityId(int windowId, long nodeId) {
2414 final AutofillClient client = getClient();
2415 if (client == null) {
2416 return null;
2417 }
2418 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2419 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2420 }
2421
2422 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2423 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2424 if (provider == null) {
2425 return null;
2426 }
2427 return provider.createAccessibilityNodeInfo(virtualId);
2428 }
2429
2430 private boolean isVirtualNode(int nodeId) {
2431 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2432 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2433 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002434 }
2435
Felipe Lemee6010f22017-03-03 11:19:51 -08002436 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002437 * View tracking information. Once all tracked views become invisible the session is finished.
2438 */
2439 private class TrackedViews {
2440 /** Visible tracked views */
2441 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2442
2443 /** Invisible tracked views */
2444 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2445
2446 /**
2447 * Check if set is null or value is in set.
2448 *
2449 * @param set The set or null (== empty set)
2450 * @param value The value that might be in the set
2451 *
2452 * @return {@code true} iff set is not empty and value is in set
2453 */
Felipe Leme27e20222017-05-18 15:24:11 -07002454 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002455 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2456 return set != null && set.contains(value);
2457 }
2458
2459 /**
2460 * Add a value to a set. If set is null, create a new set.
2461 *
2462 * @param set The set or null (== empty set)
2463 * @param valueToAdd The value to add
2464 *
2465 * @return The set including the new value. If set was {@code null}, a set containing only
2466 * the new value.
2467 */
Felipe Leme27e20222017-05-18 15:24:11 -07002468 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002469 @NonNull
2470 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2471 if (set == null) {
2472 set = new ArraySet<>(1);
2473 }
2474
2475 set.add(valueToAdd);
2476
2477 return set;
2478 }
2479
2480 /**
2481 * Remove a value from a set.
2482 *
2483 * @param set The set or null (== empty set)
2484 * @param valueToRemove The value to remove
2485 *
2486 * @return The set without the removed value. {@code null} if set was null, or is empty
2487 * after removal.
2488 */
Felipe Leme27e20222017-05-18 15:24:11 -07002489 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002490 @Nullable
2491 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2492 if (set == null) {
2493 return null;
2494 }
2495
2496 set.remove(valueToRemove);
2497
2498 if (set.isEmpty()) {
2499 return null;
2500 }
2501
2502 return set;
2503 }
2504
2505 /**
2506 * Set the tracked views.
2507 *
2508 * @param trackedIds The views to be tracked
2509 */
Felipe Leme27e20222017-05-18 15:24:11 -07002510 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002511 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002512 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002513 final boolean[] isVisible;
2514
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002515 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002516 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002517 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002518 } else {
2519 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002520 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002521 }
2522
Felipe Leme27e20222017-05-18 15:24:11 -07002523 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002524 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002525 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002526
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002527 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002528 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002529 } else {
2530 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2531 }
2532 }
2533 }
2534
Felipe Leme9f9ee252017-04-27 13:56:22 -07002535 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002536 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002537 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2538 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2539 }
2540
2541 if (mVisibleTrackedIds == null) {
2542 finishSessionLocked();
2543 }
2544 }
2545
2546 /**
2547 * Called when a {@link View view's} visibility changes.
2548 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002549 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002550 * @param isVisible visible if the view is visible in the view hierarchy.
2551 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002552 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002553 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002554 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002555 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002556 + isVisible);
2557 }
2558
Dake Gu67decfa2017-12-27 11:48:08 -08002559 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002560 if (isVisible) {
2561 if (isInSet(mInvisibleTrackedIds, id)) {
2562 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2563 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2564 }
2565 } else {
2566 if (isInSet(mVisibleTrackedIds, id)) {
2567 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2568 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2569 }
2570 }
2571 }
2572
2573 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002574 if (sVerbose) {
2575 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2576 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002577 finishSessionLocked();
2578 }
2579 }
2580
2581 /**
2582 * Called once the client becomes visible.
2583 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002584 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002585 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002586 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002587 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002588 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002589 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002590 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002591 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2592 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2593 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07002594 if (sVerbose) {
2595 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
2596 + " vis=" + mVisibleTrackedIds);
2597 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002598 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002599 final ArrayList<AutofillId> orderedInvisibleIds =
2600 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002601 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2602 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002603
2604 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2605 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2606 final AutofillId id = orderedInvisibleIds.get(i);
2607 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002608 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2609
Felipe Leme9f9ee252017-04-27 13:56:22 -07002610 if (sDebug) {
2611 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002612 }
2613 } else {
2614 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2615 }
2616 }
2617 }
2618
2619 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002620 final ArrayList<AutofillId> orderedVisibleIds =
2621 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002622 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2623 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002624
2625 final int numVisibleTrackedIds = orderedVisibleIds.size();
2626 for (int i = 0; i < numVisibleTrackedIds; i++) {
2627 final AutofillId id = orderedVisibleIds.get(i);
2628
2629 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002630 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2631 } else {
2632 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2633
Felipe Leme9f9ee252017-04-27 13:56:22 -07002634 if (sDebug) {
2635 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002636 }
2637 }
2638 }
2639 }
2640
2641 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2642 mVisibleTrackedIds = updatedVisibleTrackedIds;
2643 }
2644
2645 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002646 if (sVerbose) {
2647 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
2648 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002649 finishSessionLocked();
2650 }
2651 }
2652 }
2653
2654 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002655 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002656 *
2657 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002658 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002659 */
2660 public abstract static class AutofillCallback {
2661
2662 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002663 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2664 EVENT_INPUT_SHOWN,
2665 EVENT_INPUT_HIDDEN,
2666 EVENT_INPUT_UNAVAILABLE
2667 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002668 @Retention(RetentionPolicy.SOURCE)
2669 public @interface AutofillEventType {}
2670
2671 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002672 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002673 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002674 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002675 * should be hidden upon receiving this event.
2676 */
2677 public static final int EVENT_INPUT_SHOWN = 1;
2678
2679 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002680 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002681 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002682 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002683 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2684 */
2685 public static final int EVENT_INPUT_HIDDEN = 2;
2686
2687 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002688 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002689 * autofill is not available.
2690 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002691 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002692 * to avoid flickering, it could shown it upon receiving this event.
2693 */
2694 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2695
2696 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002697 * Called after a change in the autofill state associated with a view.
2698 *
2699 * @param view view associated with the change.
2700 *
2701 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2702 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002703 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2704 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002705
2706 /**
2707 * Called after a change in the autofill state associated with a virtual view.
2708 *
2709 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002710 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002711 *
2712 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2713 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002714 public void onAutofillEvent(@NonNull View view, int virtualId,
2715 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002716 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002717 }
2718
Felipe Leme640f30a2017-03-06 15:44:06 -08002719 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2720 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002721
Felipe Leme640f30a2017-03-06 15:44:06 -08002722 AutofillManagerClient(AutofillManager autofillManager) {
2723 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002724 }
2725
2726 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002727 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002728 final AutofillManager afm = mAfm.get();
2729 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002730 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002731 }
2732 }
2733
2734 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002735 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002736 final AutofillManager afm = mAfm.get();
2737 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002738 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002739 }
2740 }
2741
2742 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002743 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2744 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002745 final AutofillManager afm = mAfm.get();
2746 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002747 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002748 }
2749 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002750
2751 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002752 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2753 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002754 final AutofillManager afm = mAfm.get();
2755 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002756 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2757 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002758 }
2759 }
2760
2761 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002762 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002763 final AutofillManager afm = mAfm.get();
2764 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002765 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07002766 }
2767 }
2768
2769 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002770 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002771 final AutofillManager afm = mAfm.get();
2772 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002773 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002774 }
2775 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002776
2777 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08002778 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
2779 final AutofillManager afm = mAfm.get();
2780 if (afm != null) {
2781 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
2782 }
2783 }
2784
2785 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002786 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002787 final AutofillManager afm = mAfm.get();
2788 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002789 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002790 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002791 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002792 } catch (IntentSender.SendIntentException e) {
2793 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2794 }
2795 });
2796 }
2797 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002798
2799 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002800 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002801 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2802 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002803 final AutofillManager afm = mAfm.get();
2804 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002805 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2806 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002807 }
2808 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002809
2810 @Override
2811 public void setSaveUiState(int sessionId, boolean shown) {
2812 final AutofillManager afm = mAfm.get();
2813 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002814 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2815 }
2816 }
2817
2818 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002819 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002820 final AutofillManager afm = mAfm.get();
2821 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002822 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002823 }
2824 }
Svet Ganov782043c2017-02-11 00:52:02 +00002825 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002826}