blob: 8b3ce42b076b7154cfd4c22639ec16acf2aad06f [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 Leme5cd660d2018-09-17 09:46:13 -070047import android.util.Slog;
Felipe Leme4753bb02017-03-22 20:24:00 -070048import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080049import android.view.Choreographer;
Dake Gu6a20a192018-02-08 12:09:30 -080050import android.view.KeyEvent;
Felipe Leme3461d3c2017-01-19 08:54:55 -080051import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080052import android.view.accessibility.AccessibilityEvent;
53import android.view.accessibility.AccessibilityManager;
54import android.view.accessibility.AccessibilityNodeInfo;
55import android.view.accessibility.AccessibilityNodeProvider;
56import android.view.accessibility.AccessibilityWindowInfo;
felipealfd3f8f62018-02-07 11:49:07 +010057
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070058import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070059import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070060import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080061import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080062import com.android.internal.util.Preconditions;
felipealfd3f8f62018-02-07 11:49:07 +010063
Svetoslav Ganov24c90452017-12-27 15:17:14 -080064import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070065
Svetoslav Ganov24c90452017-12-27 15:17:14 -080066import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070067import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080068import java.lang.annotation.Retention;
69import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000070import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070071import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080072import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080073import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000074import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070075import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000076
Felipe Leme67e62092018-02-15 14:47:31 -080077//TODO: use java.lang.ref.Cleaner once Android supports Java 9
felipealfd3f8f62018-02-07 11:49:07 +010078import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080079
Felipe Leme3461d3c2017-01-19 08:54:55 -080080/**
Laura Davis43e75d92018-08-10 15:46:06 -070081 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
82 * integrate with the Autofill Framework lifecycle.
83 *
84 * <p>To learn about using Autofill in your app, read
85 * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
86 *
87 * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070088 *
Felipe Leme744976e2017-07-31 11:34:14 -070089 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
Laura Davis43e75d92018-08-10 15:46:06 -070090 * activity context. The autofill context is created when one of the following methods is called for
Felipe Leme744976e2017-07-31 11:34:14 -070091 * the first time in an activity context, and the current user has an enabled autofill service:
92 *
93 * <ul>
94 * <li>{@link #notifyViewEntered(View)}
95 * <li>{@link #notifyViewEntered(View, int, Rect)}
96 * <li>{@link #requestAutofill(View)}
97 * </ul>
98 *
Laura Davis43e75d92018-08-10 15:46:06 -070099 * <p>Typically, the context is automatically created when the first view of the activity is
Felipe Leme744976e2017-07-31 11:34:14 -0700100 * focused because {@code View.onFocusChanged()} indirectly calls
101 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
102 * explicitly create it (for example, a custom view developer could offer a contextual menu action
103 * in a text-field view to let users manually request autofill).
104 *
105 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
106 * that represents the view hierarchy by calling
107 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
108 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
109 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
110 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
111 * the hierarchy.
112 *
113 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
114 * parses it looking for views that can be autofilled. If the service finds such views, it returns
115 * a data structure to the Android System containing the following optional info:
116 *
117 * <ul>
118 * <li>Datasets used to autofill subsets of views in the activity.
119 * <li>Id of views that the service can save their values for future autofilling.
120 * </ul>
121 *
122 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700123 * UI associated with the view, when the view is focused on and is part of a dataset.
124 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700125 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700126 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700127 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
128 *
129 * <p>When the service returns ids of savable views, the Android System keeps track of changes
130 * made to these views, so they can be used to determine if the autofill save UI is shown later.
131 *
132 * <p>The context is then finished when one of the following occurs:
133 *
134 * <ul>
135 * <li>{@link #commit()} is called or all savable views are gone.
136 * <li>{@link #cancel()} is called.
137 * </ul>
138 *
139 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700140 * 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 -0700141 * option to Save, the current value of the views is then sent to the autofill service.
142 *
Laura Davis43e75d92018-08-10 15:46:06 -0700143 * <h3 id="additional-notes">Additional notes</h3>
144 *
145 * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800146 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600147@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700148@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800149public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800150
Felipe Leme640f30a2017-03-06 15:44:06 -0800151 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800152
Svet Ganov782043c2017-02-11 00:52:02 +0000153 /**
154 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700155 *
Svet Ganov782043c2017-02-11 00:52:02 +0000156 * <p>
157 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000158 */
159 public static final String EXTRA_ASSIST_STRUCTURE =
160 "android.view.autofill.extra.ASSIST_STRUCTURE";
161
162 /**
163 * Intent extra: The result of an authentication operation. It is
164 * either a fully populated {@link android.service.autofill.FillResponse}
165 * or a fully populated {@link android.service.autofill.Dataset} if
166 * a response or a dataset is being authenticated respectively.
167 *
168 * <p>
169 * Type: {@link android.service.autofill.FillResponse} or a
170 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000171 */
172 public static final String EXTRA_AUTHENTICATION_RESULT =
173 "android.view.autofill.extra.AUTHENTICATION_RESULT";
174
Felipe Leme7320ca92017-03-29 15:09:54 -0700175 /**
176 * Intent extra: The optional extras provided by the
177 * {@link android.service.autofill.AutofillService}.
178 *
179 * <p>For example, when the service responds to a {@link
180 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
181 * a {@code FillResponse} that requires authentication, the Intent that launches the
182 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700183 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700184 *
Felipe Lemea9372382017-10-09 14:42:36 -0700185 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
186 * can also add this bundle to the {@link Intent} set as the
187 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
188 * so the bundle can be recovered later on
189 * {@link android.service.autofill.SaveRequest#getClientState()}.
190 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700191 * <p>
192 * Type: {@link android.os.Bundle}
193 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700194 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600195 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700196
Felipe Lemec24a56a2017-08-03 14:27:57 -0700197 /** @hide */
198 public static final String EXTRA_RESTORE_SESSION_TOKEN =
199 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
200
201 private static final String SESSION_ID_TAG = "android:sessionId";
202 private static final String STATE_TAG = "android:state";
203 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
204
Felipe Leme0aa4c502017-04-26 12:36:01 -0700205 /** @hide */ public static final int ACTION_START_SESSION = 1;
206 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
207 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
208 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800209
Felipe Leme9f9ee252017-04-27 13:56:22 -0700210
211 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
212 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
213 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
214
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700215 /** Which bits in an authentication id are used for the dataset id */
216 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
217 /** How many bits in an authentication id are used for the dataset id */
218 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
219 /** @hide The index for an undefined data set */
220 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
221
222 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700223 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
224 *
225 * @hide
226 */
227 public static final int PENDING_UI_OPERATION_CANCEL = 1;
228
229 /**
230 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
231 *
232 * @hide
233 */
234 public static final int PENDING_UI_OPERATION_RESTORE = 2;
235
236 /**
237 * Initial state of the autofill context, set when there is no session (i.e., when
238 * {@link #mSessionId} is {@link #NO_SESSION}).
239 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700240 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
241 * the server.
242 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700243 * @hide
244 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700245 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700246
247 /**
248 * State where the autofill context hasn't been {@link #commit() finished} nor
249 * {@link #cancel() canceled} yet.
250 *
251 * @hide
252 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700253 public static final int STATE_ACTIVE = 1;
254
255 /**
256 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700257 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700258 *
259 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
260 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
261 *
262 * @hide
263 */
264 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700265
266 /**
267 * State where the autofill context has been {@link #commit() finished} but the server still has
268 * a session because the Save UI hasn't been dismissed yet.
269 *
270 * @hide
271 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700272 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700273
274 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700275 * State where the autofill is disabled because the service cannot autofill the activity at all.
276 *
277 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
278 * (and {@link #requestAutofill(View, int, Rect)}).
279 *
280 * @hide
281 */
282 public static final int STATE_DISABLED_BY_SERVICE = 4;
283
284 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700285 * Same as {@link #STATE_UNKNOWN}, but used on
286 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
287 * the URL bar changed on client mode
288 *
289 * @hide
290 */
291 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
292
293
294 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800295 * Timeout in ms for calls to the field classification service.
296 * @hide
297 */
298 public static final int FC_SERVICE_TIMEOUT = 5000;
299
300 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700301 * Makes an authentication id from a request id and a dataset id.
302 *
303 * @param requestId The request id.
304 * @param datasetId The dataset id.
305 * @return The authentication id.
306 * @hide
307 */
308 public static int makeAuthenticationId(int requestId, int datasetId) {
309 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
310 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
311 }
312
313 /**
314 * Gets the request id from an authentication id.
315 *
316 * @param authRequestId The authentication id.
317 * @return The request id.
318 * @hide
319 */
320 public static int getRequestIdFromAuthenticationId(int authRequestId) {
321 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
322 }
323
324 /**
325 * Gets the dataset id from an authentication id.
326 *
327 * @param authRequestId The authentication id.
328 * @return The dataset id.
329 * @hide
330 */
331 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
332 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
333 }
334
Felipe Leme4753bb02017-03-22 20:24:00 -0700335 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000336
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700337 /**
338 * There is currently no session running.
339 * {@hide}
340 */
341 public static final int NO_SESSION = Integer.MIN_VALUE;
342
Svet Ganov782043c2017-02-11 00:52:02 +0000343 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700344
345 private final Object mLock = new Object();
346
347 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000348 private IAutoFillManagerClient mServiceClient;
349
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700350 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900351 private Cleaner mServiceClientCleaner;
352
353 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800354 private AutofillCallback mCallback;
355
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700356 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000357
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700358 @GuardedBy("mLock")
359 private int mSessionId = NO_SESSION;
360
361 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700362 private int mState = STATE_UNKNOWN;
363
364 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000365 private boolean mEnabled;
366
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700367 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700368 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700369 @Nullable private ParcelableMap mLastAutofilledData;
370
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700371 /** If view tracking is enabled, contains the tracking state */
372 @GuardedBy("mLock")
373 @Nullable private TrackedViews mTrackedViews;
374
Felipe Leme27e20222017-05-18 15:24:11 -0700375 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
376 @GuardedBy("mLock")
377 @Nullable private ArraySet<AutofillId> mFillableIds;
378
Dake Gub0fa3782018-02-26 12:25:14 -0800379 /** id of last requested autofill ui */
380 @Nullable private AutofillId mIdShownFillUi;
381
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800382 /**
383 * Views that were already "entered" - if they're entered again when the session is not active,
384 * they're ignored
385 * */
386 @GuardedBy("mLock")
387 @Nullable private ArraySet<AutofillId> mEnteredIds;
388
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700389 /** If set, session is commited when the field is clicked. */
390 @GuardedBy("mLock")
391 @Nullable private AutofillId mSaveTriggerId;
392
Dake Gu67decfa2017-12-27 11:48:08 -0800393 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
394 @GuardedBy("mLock")
395 private boolean mOnInvisibleCalled;
396
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700397 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
398 @GuardedBy("mLock")
399 private boolean mSaveOnFinish;
400
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800401 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
402 @GuardedBy("mLock")
403 private CompatibilityBridge mCompatibilityBridge;
404
Svet Ganov782043c2017-02-11 00:52:02 +0000405 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800406 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000407 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000408 * Asks the client to start an authentication flow.
409 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700410 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000411 * @param intent The authentication intent.
412 * @param fillInIntent The authentication fill-in intent.
413 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800414 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700415 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800416
417 /**
418 * Tells the client this manager has state to be reset.
419 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800420 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700421
422 /**
423 * Request showing the autofill UI.
424 *
425 * @param anchor The real view the UI needs to anchor to.
426 * @param width The width of the fill UI content.
427 * @param height The height of the fill UI content.
428 * @param virtualBounds The bounds of the virtual decendant of the anchor.
429 * @param presenter The presenter that controls the fill UI window.
430 * @return Whether the UI was shown.
431 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800432 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700433 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
434
435 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800436 * Dispatch unhandled keyevent from Autofill window
437 * @param anchor The real view the UI needs to anchor to.
438 * @param keyEvent Unhandled KeyEvent from autofill window.
439 */
440 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
441
442 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700443 * Request hiding the autofill UI.
444 *
445 * @return Whether the UI was hidden.
446 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800447 boolean autofillClientRequestHideFillUi();
448
449 /**
450 * Gets whether the fill UI is currenlty being shown.
451 *
452 * @return Whether the fill UI is currently being shown
453 */
454 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700455
456 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700457 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700458 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700459 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700460 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800461 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700462
463 /**
464 * Checks is the client is currently visible as understood by autofill.
465 *
466 * @return {@code true} if the client is currently visible
467 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800468 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700469
470 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800471 * Client might disable enter/exit event e.g. when activity is paused.
472 */
473 boolean isDisablingEnterExitEventForAutofill();
474
475 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700476 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700477 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800478 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700479 *
Felipe Leme27e20222017-05-18 15:24:11 -0700480 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700481 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800482 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
483 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700484
485 /**
486 * Finds a view by traversing the hierarchies of the client.
487 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800488 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700489 *
490 * @return The view, or {@code null} if not found
491 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800492 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
493
494 /**
495 * Finds a view by a11y id in a given client window.
496 *
497 * @param viewId The accessibility id of the views to find
498 * @param windowId The accessibility window id where to search
499 *
500 * @return The view, or {@code null} if not found
501 */
502 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700503
504 /**
505 * Runs the specified action on the UI thread.
506 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800507 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700508
509 /**
510 * Gets the complete component name of this client.
511 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800512 ComponentName autofillClientGetComponentName();
513
514 /**
515 * Gets the activity token
516 */
517 @Nullable IBinder autofillClientGetActivityToken();
518
519 /**
520 * @return Whether compatibility mode is enabled.
521 */
Felipe Leme42b97932018-02-20 13:04:31 -0800522 boolean autofillClientIsCompatibilityModeEnabled();
523
524 /**
525 * Gets the next unique autofill ID.
526 *
527 * <p>Typically used to manage views whose content is recycled - see
528 * {@link View#setAutofillId(AutofillId)} for more info.
529 *
530 * @return An ID that is unique in the activity.
531 */
532 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000533 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800534
535 /**
536 * @hide
537 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800538 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800539 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800540 mService = service;
541 }
542
543 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800544 * @hide
545 */
546 public void enableCompatibilityMode() {
547 synchronized (mLock) {
548 // The accessibility manager is a singleton so we may need to plug
549 // different bridge based on which activity is currently focused
550 // in the current process. Since compat would be rarely used, just
551 // create and register a new instance every time.
Felipe Leme5cd660d2018-09-17 09:46:13 -0700552 if (sDebug) {
553 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
554 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800555 mCompatibilityBridge = new CompatibilityBridge();
556 }
557 }
558
559 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700560 * Restore state after activity lifecycle
561 *
562 * @param savedInstanceState The state to be restored
563 *
564 * {@hide}
565 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700566 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700567 if (!hasAutofillFeature()) {
568 return;
569 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700570 synchronized (mLock) {
571 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
572
Felipe Lemec24a56a2017-08-03 14:27:57 -0700573 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700574 Log.w(TAG, "New session was started before onCreate()");
575 return;
576 }
577
578 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700579 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700580
581 if (mSessionId != NO_SESSION) {
582 ensureServiceClientAddedIfNeededLocked();
583
Felipe Leme637e05e2017-12-06 12:09:37 -0800584 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700585 if (client != null) {
586 try {
587 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800588 client.autofillClientGetActivityToken(),
589 mServiceClient.asBinder());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700590
591 if (!sessionWasRestored) {
592 Log.w(TAG, "Session " + mSessionId + " could not be restored");
593 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700594 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700595 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700596 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700597 Log.d(TAG, "session " + mSessionId + " was restored");
598 }
599
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800600 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700601 }
602 } catch (RemoteException e) {
603 Log.e(TAG, "Could not figure out if there was an autofill session", e);
604 }
605 }
606 }
607 }
608 }
609
610 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700611 * Called once the client becomes visible.
612 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800613 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700614 *
615 * {@hide}
616 */
617 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800618 // This gets called when the client just got visible at which point the visibility
619 // of the tracked views may not have been computed (due to a pending layout, etc).
620 // While generally we have no way to know when the UI has settled. We will evaluate
621 // the tracked views state at the end of next frame to guarantee that everything
622 // that may need to be laid out is laid out.
623 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
624 synchronized (mLock) {
625 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
626 mTrackedViews.onVisibleForAutofillChangedLocked();
627 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700628 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800629 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700630 }
631
632 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800633 * Called once the client becomes invisible.
634 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800635 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800636 *
637 * {@hide}
638 */
639 public void onInvisibleForAutofill() {
640 synchronized (mLock) {
641 mOnInvisibleCalled = true;
642 }
643 }
644
645 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700646 * Save state before activity lifecycle
647 *
648 * @param outState Place to store the state
649 *
650 * {@hide}
651 */
652 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700653 if (!hasAutofillFeature()) {
654 return;
655 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700656 synchronized (mLock) {
657 if (mSessionId != NO_SESSION) {
658 outState.putInt(SESSION_ID_TAG, mSessionId);
659 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700660 if (mState != STATE_UNKNOWN) {
661 outState.putInt(STATE_TAG, mState);
662 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700663 if (mLastAutofilledData != null) {
664 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
665 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700666 }
667 }
668
669 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800670 * @hide
671 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800672 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100673 public boolean isCompatibilityModeEnabledLocked() {
674 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800675 }
676
677 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700678 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700679 *
680 * <p>Typically used to determine whether the option to explicitly request autofill should
681 * be offered - see {@link #requestAutofill(View)}.
682 *
683 * @return whether autofill is enabled for the current user.
684 */
685 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800686 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700687 return false;
688 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700689 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800690 if (isDisabledByServiceLocked()) {
691 return false;
692 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700693 ensureServiceClientAddedIfNeededLocked();
694 return mEnabled;
695 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700696 }
697
698 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700699 * Should always be called from {@link AutofillService#getFillEventHistory()}.
700 *
701 * @hide
702 */
703 @Nullable public FillEventHistory getFillEventHistory() {
704 try {
705 return mService.getFillEventHistory();
706 } catch (RemoteException e) {
707 e.rethrowFromSystemServer();
708 return null;
709 }
710 }
711
712 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700713 * Explicitly requests a new autofill context.
714 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700715 * <p>Normally, the autofill context is automatically started if necessary when
716 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
717 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
718 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700719 *
720 * @param view view requesting the new autofill context.
721 */
722 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700723 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700724 }
725
726 /**
727 * Explicitly requests a new autofill context for virtual views.
728 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700729 * <p>Normally, the autofill context is automatically started if necessary when
730 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
731 * cases where it must be explicitly started. For example, when the virtual view offers an
732 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700733 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700734 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
735 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
736 * the absolute bounds could be calculated by:
737 *
738 * <pre class="prettyprint">
739 * int offset[] = new int[2];
740 * getLocationOnScreen(offset);
741 * Rect absBounds = new Rect(bounds.left + offset[0],
742 * bounds.top + offset[1],
743 * bounds.right + offset[0], bounds.bottom + offset[1]);
744 * </pre>
745 *
746 * @param view the virtual view parent.
747 * @param virtualId id identifying the virtual child inside the parent view.
748 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700749 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700750 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
751 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700752 }
753
Felipe Leme2ac463e2017-03-13 14:06:25 -0700754 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700755 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800756 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700757 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800758 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700759 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700760 notifyViewEntered(view, 0);
761 }
762
Andreas Gampe3f24e692018-02-05 13:24:28 -0800763 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800764 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800765 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700766 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800767 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
768 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700769 }
770 return true;
771 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800772 if (isFinishedLocked()) {
773 // Session already finished: ignore if automatic request and view already entered
774 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
775 && mEnteredIds.contains(id)) {
776 if (sVerbose) {
777 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
778 + ") on state " + getStateAsStringLocked()
779 + " because view was already entered: " + mEnteredIds);
780 }
781 return true;
782 }
783 }
784 if (sVerbose) {
785 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
786 + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
Felipe Leme17292d12017-10-24 14:03:10 -0700787 }
788 return false;
789 }
790
Dake Gu67decfa2017-12-27 11:48:08 -0800791 private boolean isClientVisibleForAutofillLocked() {
792 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800793 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800794 }
795
796 private boolean isClientDisablingEnterExitEvent() {
797 final AutofillClient client = getClient();
798 return client != null && client.isDisablingEnterExitEventForAutofill();
799 }
800
Felipe Lemed1146422017-04-26 10:17:05 -0700801 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700802 if (!hasAutofillFeature()) {
803 return;
804 }
Dake Gu67decfa2017-12-27 11:48:08 -0800805 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700806 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800807 callback = notifyViewEnteredLocked(view, flags);
808 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700809
Dake Gu67decfa2017-12-27 11:48:08 -0800810 if (callback != null) {
811 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
812 }
813 }
Svet Ganov782043c2017-02-11 00:52:02 +0000814
Dake Gu67decfa2017-12-27 11:48:08 -0800815 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800816 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800817 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800818 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800819 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800820
821 AutofillCallback callback = null;
822
823 ensureServiceClientAddedIfNeededLocked();
824
825 if (!mEnabled) {
826 if (mCallback != null) {
827 callback = mCallback;
828 }
829 } else {
830 // don't notify entered when Activity is already in background
831 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700832 final AutofillValue value = view.getAutofillValue();
833
Felipe Lemec24a56a2017-08-03 14:27:57 -0700834 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700835 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700836 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700837 } else {
838 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700839 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700840 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800841 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700842 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800843 }
Dake Gu67decfa2017-12-27 11:48:08 -0800844 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800845 }
846
847 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700848 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800849 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700850 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800851 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700852 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700853 if (!hasAutofillFeature()) {
854 return;
855 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700856 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800857 notifyViewExitedLocked(view);
858 }
859 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800860
Andreas Gampe3f24e692018-02-05 13:24:28 -0800861 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800862 void notifyViewExitedLocked(@NonNull View view) {
863 ensureServiceClientAddedIfNeededLocked();
864
865 if (mEnabled && isActiveLocked()) {
866 // dont notify exited when Activity is already in background
867 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -0800868 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800869
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700870 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700871 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700872 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800873 }
874 }
875
876 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700877 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700878 *
879 * @param view {@link View} that was exited.
880 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700881 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700882 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
883 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
884 }
885
886 /**
887 * Called when a 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 */
893 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
894 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
895 }
896
897 /**
898 * Called when a view/virtual view's visibility changed.
899 *
900 * @param view {@link View} that was exited.
901 * @param virtualId id identifying the virtual child inside the parent view.
902 * @param isVisible visible if the view is visible in the view hierarchy.
903 * @param virtual Whether the view is virtual.
904 */
905 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
906 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700907 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700908 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700909 final AutofillId id = virtual ? getAutofillId(view, virtualId)
910 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -0800911 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700912 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700913 if (mFillableIds.contains(id)) {
914 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
915 requestHideFillUi(id, view);
916 }
917 }
918 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800919 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -0800920 } else if (sVerbose) {
921 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -0700922 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700923 }
924 }
925 }
926
927 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700928 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800929 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700930 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
931 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
932 * the absolute bounds could be calculated by:
933 *
934 * <pre class="prettyprint">
935 * int offset[] = new int[2];
936 * getLocationOnScreen(offset);
937 * Rect absBounds = new Rect(bounds.left + offset[0],
938 * bounds.top + offset[1],
939 * bounds.right + offset[0], bounds.bottom + offset[1]);
940 * </pre>
941 *
942 * @param view the virtual view parent.
943 * @param virtualId id identifying the virtual child inside the parent view.
944 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800945 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700946 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
947 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700948 }
949
Felipe Leme6dcec872017-05-25 11:24:23 -0700950 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700951 if (!hasAutofillFeature()) {
952 return;
953 }
Dake Gu67decfa2017-12-27 11:48:08 -0800954 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700955 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800956 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
957 }
Felipe Leme17292d12017-10-24 14:03:10 -0700958
Dake Gu67decfa2017-12-27 11:48:08 -0800959 if (callback != null) {
960 callback.onAutofillEvent(view, virtualId,
961 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
962 }
963 }
Svet Ganov782043c2017-02-11 00:52:02 +0000964
Dake Gu67decfa2017-12-27 11:48:08 -0800965 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800966 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800967 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
968 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800969 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -0800970 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800971 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -0800972
973 ensureServiceClientAddedIfNeededLocked();
974
975 if (!mEnabled) {
976 if (mCallback != null) {
977 callback = mCallback;
978 }
979 } else {
980 // don't notify entered when Activity is already in background
981 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700982 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700983 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700984 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700985 } else {
986 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700987 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700988 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800989 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700990 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800991 }
Dake Gu67decfa2017-12-27 11:48:08 -0800992 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800993 }
994
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800995 @GuardedBy("mLock")
996 private void addEnteredIdLocked(@NonNull AutofillId id) {
997 if (mEnteredIds == null) {
998 mEnteredIds = new ArraySet<>(1);
999 }
1000 mEnteredIds.add(id);
1001 }
1002
Philip P. Moltmann44611812017-02-23 12:52:46 -08001003 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001004 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001005 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001006 * @param view the virtual view parent.
1007 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001008 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001009 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001010 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001011 if (!hasAutofillFeature()) {
1012 return;
1013 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001014 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001015 notifyViewExitedLocked(view, virtualId);
1016 }
1017 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001018
Andreas Gampe3f24e692018-02-05 13:24:28 -08001019 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001020 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1021 ensureServiceClientAddedIfNeededLocked();
1022
1023 if (mEnabled && isActiveLocked()) {
1024 // don't notify exited when Activity is already in background
1025 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -07001026 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001027
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001028 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001029 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001030 }
Svet Ganov782043c2017-02-11 00:52:02 +00001031 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001032 }
1033
1034 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001035 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001036 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001037 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001038 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001039 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001040 if (!hasAutofillFeature()) {
1041 return;
1042 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001043 AutofillId id = null;
1044 boolean valueWasRead = false;
1045 AutofillValue value = null;
1046
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001047 synchronized (mLock) {
1048 // If the session is gone some fields might still be highlighted, hence we have to
1049 // remove the isAutofilled property even if no sessions are active.
1050 if (mLastAutofilledData == null) {
1051 view.setAutofilled(false);
1052 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001053 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001054 if (mLastAutofilledData.containsKey(id)) {
1055 value = view.getAutofillValue();
1056 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001057
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001058 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1059 view.setAutofilled(true);
1060 } else {
1061 view.setAutofilled(false);
1062 mLastAutofilledData.remove(id);
1063 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001064 } else {
1065 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001066 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001067 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001068
Felipe Lemec24a56a2017-08-03 14:27:57 -07001069 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001070 if (sVerbose) {
1071 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1072 + "): ignoring on state " + getStateAsStringLocked());
Felipe Lemec7b45292017-09-19 09:06:20 -07001073 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001074 return;
1075 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001076
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001077 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001078 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001079 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001080
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001081 if (!valueWasRead) {
1082 value = view.getAutofillValue();
1083 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001084
Felipe Leme0aa4c502017-04-26 12:36:01 -07001085 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001086 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001087 }
1088
Felipe Lemebab851c2017-02-03 18:45:08 -08001089 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001090 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001091 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001092 * @param view the virtual view parent.
1093 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001094 * @param value new value of the child.
1095 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001096 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001097 if (!hasAutofillFeature()) {
1098 return;
1099 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001100 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001101 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001102 if (sVerbose) {
1103 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1104 + "): ignoring on state " + getStateAsStringLocked());
1105 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001106 return;
1107 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001108
Felipe Leme6dcec872017-05-25 11:24:23 -07001109 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001110 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001111 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001112 }
1113
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001114 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001115 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001116 *
Felipe Leme67e62092018-02-15 14:47:31 -08001117 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001118 */
Felipe Leme67e62092018-02-15 14:47:31 -08001119 public void notifyViewClicked(@NonNull View view) {
1120 notifyViewClicked(view.getAutofillId());
1121 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001122
Felipe Leme67e62092018-02-15 14:47:31 -08001123 /**
1124 * Called to indicate a virtual view has been clicked.
1125 *
1126 * @param view the virtual view parent.
1127 * @param virtualId id identifying the virtual child inside the parent view.
1128 */
1129 public void notifyViewClicked(@NonNull View view, int virtualId) {
1130 notifyViewClicked(getAutofillId(view, virtualId));
1131 }
1132
1133 private void notifyViewClicked(AutofillId id) {
1134 if (!hasAutofillFeature()) {
1135 return;
1136 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001137 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1138
1139 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001140 if (!mEnabled || !isActiveLocked()) {
1141 return;
1142 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001143 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1144 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1145 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001146 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001147 }
1148 }
1149 }
1150
1151 /**
1152 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1153 *
1154 * @hide
1155 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001156 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001157 if (!hasAutofillFeature()) {
1158 return;
1159 }
1160 synchronized (mLock) {
1161 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001162 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001163 commitLocked();
1164 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001165 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001166 cancelLocked();
1167 }
1168 }
1169 }
1170
Felipe Lemebab851c2017-02-03 18:45:08 -08001171 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001172 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001173 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001174 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1175 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1176 * that represent the HTML elements, it should call this method after the form is submitted and
1177 * another page is rendered.
1178 *
1179 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1180 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001181 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001182 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001183 if (!hasAutofillFeature()) {
1184 return;
1185 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001186 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001187 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001188 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001189 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001190 }
1191
Andreas Gampe3f24e692018-02-05 13:24:28 -08001192 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001193 private void commitLocked() {
1194 if (!mEnabled && !isActiveLocked()) {
1195 return;
1196 }
1197 finishSessionLocked();
1198 }
1199
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001200 /**
1201 * Called to indicate the current autofill context should be cancelled.
1202 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001203 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1204 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1205 * that represent the HTML elements, it should call this method if the user does not post the
1206 * form but moves to another form in this page.
1207 *
1208 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1209 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001210 */
1211 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001212 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001213 if (!hasAutofillFeature()) {
1214 return;
1215 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001216 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001217 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001218 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001219 }
1220
Andreas Gampe3f24e692018-02-05 13:24:28 -08001221 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001222 private void cancelLocked() {
1223 if (!mEnabled && !isActiveLocked()) {
1224 return;
1225 }
1226 cancelSessionLocked();
1227 }
1228
Svet Ganovf965b872017-04-28 16:34:02 -07001229 /** @hide */
1230 public void disableOwnedAutofillServices() {
1231 disableAutofillServices();
1232 }
1233
Svet Ganovf20a0372017-04-10 17:08:05 -07001234 /**
1235 * If the app calling this API has enabled autofill services they
1236 * will be disabled.
1237 */
Svet Ganovf965b872017-04-28 16:34:02 -07001238 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001239 if (!hasAutofillFeature()) {
1240 return;
1241 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001242 try {
1243 mService.disableOwnedAutofillServices(mContext.getUserId());
1244 } catch (RemoteException e) {
1245 throw e.rethrowFromSystemServer();
1246 }
1247 }
1248
Felipe Lemedb041182017-04-21 17:33:38 -07001249 /**
1250 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1251 * enabled for the current user, or {@code false} otherwise.
1252 */
1253 public boolean hasEnabledAutofillServices() {
1254 if (mService == null) return false;
1255
1256 try {
1257 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1258 } catch (RemoteException e) {
1259 throw e.rethrowFromSystemServer();
1260 }
1261 }
1262
1263 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001264 * Returns the component name of the {@link AutofillService} that is enabled for the current
1265 * user.
1266 */
1267 @Nullable
1268 public ComponentName getAutofillServiceComponentName() {
1269 if (mService == null) return null;
1270
1271 try {
1272 return mService.getAutofillServiceComponentName();
1273 } catch (RemoteException e) {
1274 throw e.rethrowFromSystemServer();
1275 }
1276 }
1277
1278 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001279 * Gets the id of the {@link UserData} used for
1280 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1281 *
1282 * <p>This method is useful when the service must check the status of the {@link UserData} in
1283 * the device without fetching the whole object.
1284 *
1285 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1286 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1287 * the user.
1288 *
1289 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1290 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1291 * service for the user.
1292 */
1293 @Nullable public String getUserDataId() {
1294 try {
1295 return mService.getUserDataId();
1296 } catch (RemoteException e) {
1297 e.rethrowFromSystemServer();
1298 return null;
1299 }
1300 }
1301
1302 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001303 * Gets the user data used for
1304 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001305 *
Felipe Leme27f45732017-12-22 09:05:22 -08001306 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1307 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1308 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001309 *
Felipe Leme452886a2017-11-27 13:09:13 -08001310 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1311 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001312 */
Felipe Leme452886a2017-11-27 13:09:13 -08001313 @Nullable public UserData getUserData() {
1314 try {
1315 return mService.getUserData();
1316 } catch (RemoteException e) {
1317 e.rethrowFromSystemServer();
1318 return null;
1319 }
1320 }
1321
1322 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001323 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001324 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001325 *
1326 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1327 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1328 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001329 */
Felipe Leme452886a2017-11-27 13:09:13 -08001330 public void setUserData(@Nullable UserData userData) {
1331 try {
1332 mService.setUserData(userData);
1333 } catch (RemoteException e) {
1334 e.rethrowFromSystemServer();
1335 }
1336 }
1337
1338 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001339 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1340 * enabled.
1341 *
1342 * <p>As field classification is an expensive operation, it could be disabled, either
1343 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1344 * permanently (for example, because the device is a low-level device).
1345 *
1346 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1347 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1348 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001349 */
Felipe Leme329d0402017-12-06 09:22:43 -08001350 public boolean isFieldClassificationEnabled() {
1351 try {
1352 return mService.isFieldClassificationEnabled();
1353 } catch (RemoteException e) {
1354 e.rethrowFromSystemServer();
1355 return false;
1356 }
1357 }
1358
1359 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001360 * Gets the name of the default algorithm used for
1361 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1362 *
1363 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1364 * set.
1365 *
1366 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1367 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1368 * the user.
1369 */
1370 @Nullable
1371 public String getDefaultFieldClassificationAlgorithm() {
1372 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001373 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001374 } catch (RemoteException e) {
1375 e.rethrowFromSystemServer();
1376 return null;
1377 }
1378 }
1379
1380 /**
1381 * Gets the name of all algorithms currently available for
1382 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1383 *
1384 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001385 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1386 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001387 */
1388 @NonNull
1389 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001390 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001391 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001392 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1393 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001394 } catch (RemoteException e) {
1395 e.rethrowFromSystemServer();
1396 return null;
1397 }
1398 }
1399
1400 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001401 * Returns {@code true} if autofill is supported by the current device and
1402 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001403 *
1404 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1405 * <ol>
1406 * <li>Low-end devices.
1407 * <li>Device policy rules that forbid its usage.
1408 * </ol>
1409 */
1410 public boolean isAutofillSupported() {
1411 if (mService == null) return false;
1412
1413 try {
1414 return mService.isServiceSupported(mContext.getUserId());
1415 } catch (RemoteException e) {
1416 throw e.rethrowFromSystemServer();
1417 }
1418 }
1419
Felipe Leme637e05e2017-12-06 12:09:37 -08001420 // Note: don't need to use locked suffix because mContext is final.
1421 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001422 final AutofillClient client = mContext.getAutofillClient();
1423 if (client == null && sDebug) {
1424 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1425 + mContext);
1426 }
1427 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001428 }
1429
Dake Gud9dbd272018-03-13 11:38:42 -07001430 /**
1431 * Check if autofill ui is showing, must be called on UI thread.
1432 * @hide
1433 */
1434 public boolean isAutofillUiShowing() {
1435 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001436 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001437 }
1438
Svet Ganov782043c2017-02-11 00:52:02 +00001439 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001440 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001441 if (!hasAutofillFeature()) {
1442 return;
1443 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001444 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001445 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1446 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1447 // service set the extra and returned RESULT_CANCELED...
1448
Felipe Leme9f9ee252017-04-27 13:56:22 -07001449 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001450
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001451 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001452 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001453 return;
1454 }
Dake Gu67decfa2017-12-27 11:48:08 -08001455 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1456 // of app activity. We enforce enter event to re-show fill ui in such case.
1457 // CTS example:
1458 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1459 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1460 if (!mOnInvisibleCalled && focusView != null
1461 && focusView.canNotifyAutofillEnterExitEvent()) {
1462 notifyViewExitedLocked(focusView);
1463 notifyViewEnteredLocked(focusView, 0);
1464 }
1465 if (data == null) {
1466 // data is set to null when result is not RESULT_OK
1467 return;
1468 }
1469
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001470 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1471 final Bundle responseData = new Bundle();
1472 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001473 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1474 if (newClientState != null) {
1475 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1476 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001477 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001478 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1479 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001480 } catch (RemoteException e) {
1481 Log.e(TAG, "Error delivering authentication result", e);
1482 }
Svet Ganov782043c2017-02-11 00:52:02 +00001483 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001484 }
1485
Felipe Leme42b97932018-02-20 13:04:31 -08001486 /**
1487 * Gets the next unique autofill ID for the activity context.
1488 *
1489 * <p>Typically used to manage views whose content is recycled - see
1490 * {@link View#setAutofillId(AutofillId)} for more info.
1491 *
1492 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1493 * the {@link Context} associated with this {@link AutofillManager}.
1494 */
1495 @Nullable
1496 public AutofillId getNextAutofillId() {
1497 final AutofillClient client = getClient();
1498 if (client == null) return null;
1499
1500 final AutofillId id = client.autofillClientGetNextAutofillId();
1501
1502 if (id == null && sDebug) {
1503 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1504 }
1505
1506 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001507 }
1508
Felipe Leme6dcec872017-05-25 11:24:23 -07001509 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001510 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001511 }
1512
Andreas Gampe3f24e692018-02-05 13:24:28 -08001513 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001514 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1515 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001516 if (sVerbose) {
1517 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001518 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001519 + ", compatMode=" + isCompatibilityModeEnabledLocked()
1520 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001521 }
Felipe Leme3103c632017-12-18 15:05:14 -08001522 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001523 if (sVerbose) {
1524 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001525 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001526 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001527 return;
1528 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001529 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001530 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001531 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001532
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001533 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001534 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme185de722018-02-13 17:25:44 -08001535 mCallback != null, flags, client.autofillClientGetComponentName(),
1536 isCompatibilityModeEnabledLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001537 if (mSessionId != NO_SESSION) {
1538 mState = STATE_ACTIVE;
1539 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001540 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001541 } catch (RemoteException e) {
1542 throw e.rethrowFromSystemServer();
1543 }
1544 }
1545
Andreas Gampe3f24e692018-02-05 13:24:28 -08001546 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001547 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001548 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001549
1550 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001551
Svet Ganov782043c2017-02-11 00:52:02 +00001552 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001553 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001554 } catch (RemoteException e) {
1555 throw e.rethrowFromSystemServer();
1556 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001557
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001558 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001559 }
1560
Andreas Gampe3f24e692018-02-05 13:24:28 -08001561 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001562 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001563 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001564
1565 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001566
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001567 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001568 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001569 } catch (RemoteException e) {
1570 throw e.rethrowFromSystemServer();
1571 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001572
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001573 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001574 }
1575
Andreas Gampe3f24e692018-02-05 13:24:28 -08001576 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001577 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001578 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001579 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001580 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001581 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001582 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001583 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001584 if (resetEnteredIds) {
1585 mEnteredIds = null;
1586 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001587 }
1588
Andreas Gampe3f24e692018-02-05 13:24:28 -08001589 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001590 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1591 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001592 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001593 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1594 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001595 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001596 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1597
Felipe Leme3461d3c2017-01-19 08:54:55 -08001598 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001599 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001600 final AutofillClient client = getClient();
1601 if (client == null) return; // NOTE: getClient() already logd it..
1602
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001603 final int newId = mService.updateOrRestartSession(
1604 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001605 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001606 mCallback != null, flags, client.autofillClientGetComponentName(),
Felipe Leme185de722018-02-13 17:25:44 -08001607 mSessionId, action, isCompatibilityModeEnabledLocked());
Felipe Leme7f33cd32017-05-11 10:10:49 -07001608 if (newId != mSessionId) {
1609 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1610 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001611 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001612 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001613 }
1614 } else {
1615 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1616 mContext.getUserId());
1617 }
1618
Felipe Leme3461d3c2017-01-19 08:54:55 -08001619 } catch (RemoteException e) {
1620 throw e.rethrowFromSystemServer();
1621 }
1622 }
Svet Ganov782043c2017-02-11 00:52:02 +00001623
Andreas Gampe3f24e692018-02-05 13:24:28 -08001624 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001625 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001626 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001627 return;
1628 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001629
Svet Ganov782043c2017-02-11 00:52:02 +00001630 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001631 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001632 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001633 final int userId = mContext.getUserId();
1634 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001635 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1636 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1637 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001638 final IAutoFillManager service = mService;
1639 final IAutoFillManagerClient serviceClient = mServiceClient;
1640 mServiceClientCleaner = Cleaner.create(this, () -> {
1641 try {
1642 service.removeClient(serviceClient, userId);
1643 } catch (RemoteException e) {
1644 }
1645 });
Svet Ganov782043c2017-02-11 00:52:02 +00001646 } catch (RemoteException e) {
1647 throw e.rethrowFromSystemServer();
1648 }
1649 }
1650 }
1651
Felipe Lemee6010f22017-03-03 11:19:51 -08001652 /**
1653 * Registers a {@link AutofillCallback} to receive autofill events.
1654 *
1655 * @param callback callback to receive events.
1656 */
1657 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001658 if (!hasAutofillFeature()) {
1659 return;
1660 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001661 synchronized (mLock) {
1662 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001663
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001664 final boolean hadCallback = mCallback != null;
1665 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001666
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001667 if (!hadCallback) {
1668 try {
1669 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1670 } catch (RemoteException e) {
1671 throw e.rethrowFromSystemServer();
1672 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001673 }
1674 }
1675 }
1676
1677 /**
1678 * Unregisters a {@link AutofillCallback} to receive autofill events.
1679 *
1680 * @param callback callback to stop receiving events.
1681 */
1682 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001683 if (!hasAutofillFeature()) {
1684 return;
1685 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001686 synchronized (mLock) {
1687 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001688
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001689 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001690
Felipe Lemee6010f22017-03-03 11:19:51 -08001691 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001692 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001693 } catch (RemoteException e) {
1694 throw e.rethrowFromSystemServer();
1695 }
1696 }
1697 }
1698
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001699 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1700 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1701 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001702 if (anchor == null) {
1703 return;
1704 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001705
1706 AutofillCallback callback = null;
1707 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001708 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001709 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001710
1711 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001712 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08001713 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001714 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08001715 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001716 }
1717 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001718 }
1719 }
1720
1721 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001722 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001723 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001724 AutofillCallback.EVENT_INPUT_SHOWN);
1725 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001726 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001727 }
1728 }
1729 }
1730
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001731 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1732 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001733 synchronized (mLock) {
1734 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001735 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001736 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001737 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1738 // before onAuthenticationResult()
1739 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001740 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001741 }
1742 }
1743 }
1744 }
1745
Dake Gu6a20a192018-02-08 12:09:30 -08001746 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
1747 final View anchor = findView(id);
1748 if (anchor == null) {
1749 return;
1750 }
1751
1752 AutofillCallback callback = null;
1753 synchronized (mLock) {
1754 if (mSessionId == sessionId) {
1755 AutofillClient client = getClient();
1756
1757 if (client != null) {
1758 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
1759 }
1760 }
1761 }
1762 }
1763
Felipe Leme51e29da2017-10-24 14:03:10 -07001764 /** @hide */
1765 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1766 /** @hide */
1767 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1768 /** @hide */
1769 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1770 /** @hide */
1771 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1772 /** @hide */
1773 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1774
1775 private void setState(int flags) {
1776 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001777 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001778 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1779 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001780 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001781 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001782 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001783 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001784 // Reset connection to system
1785 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001786 if (mServiceClientCleaner != null) {
1787 mServiceClientCleaner.clean();
1788 mServiceClientCleaner = null;
1789 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001790 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001791 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001792 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1793 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001794 }
1795
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001796 /**
1797 * Sets a view as autofilled if the current value is the {code targetValue}.
1798 *
1799 * @param view The view that is to be autofilled
1800 * @param targetValue The value we want to fill into view
1801 */
1802 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1803 AutofillValue currentValue = view.getAutofillValue();
1804 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001805 synchronized (mLock) {
1806 if (mLastAutofilledData == null) {
1807 mLastAutofilledData = new ParcelableMap(1);
1808 }
Felipe Leme42b97932018-02-20 13:04:31 -08001809 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001810 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001811 view.setAutofilled(true);
1812 }
1813 }
1814
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001815 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001816 synchronized (mLock) {
1817 if (sessionId != mSessionId) {
1818 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001819 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001820
Felipe Leme637e05e2017-12-06 12:09:37 -08001821 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001822 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001823 return;
1824 }
1825
1826 final int itemCount = ids.size();
1827 int numApplied = 0;
1828 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001829 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1830 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001831
Felipe Leme49f08ed2018-03-26 16:18:45 -07001832 ArrayList<AutofillId> failedIds = null;
1833
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001834 for (int i = 0; i < itemCount; i++) {
1835 final AutofillId id = ids.get(i);
1836 final AutofillValue value = values.get(i);
1837 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001838 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001839 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07001840 // Most likely view has been removed after the initial request was sent to the
1841 // the service; this is fine, but we need to update the view status in the
1842 // server side so it can be triggered again.
1843 Log.d(TAG, "autofill(): no View with id " + id);
1844 if (failedIds == null) {
1845 failedIds = new ArrayList<>();
1846 }
1847 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001848 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001849 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001850 if (id.isVirtual()) {
1851 if (virtualValues == null) {
1852 // Most likely there will be just one view with virtual children.
1853 virtualValues = new ArrayMap<>(1);
1854 }
1855 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1856 if (valuesByParent == null) {
1857 // We don't know the size yet, but usually it will be just a few fields...
1858 valuesByParent = new SparseArray<>(5);
1859 virtualValues.put(view, valuesByParent);
1860 }
1861 valuesByParent.put(id.getVirtualChildId(), value);
1862 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001863 // Mark the view as to be autofilled with 'value'
1864 if (mLastAutofilledData == null) {
1865 mLastAutofilledData = new ParcelableMap(itemCount - i);
1866 }
1867 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001868
1869 view.autofill(value);
1870
1871 // Set as autofilled if the values match now, e.g. when the value was updated
1872 // synchronously.
1873 // If autofill happens async, the view is set to autofilled in
1874 // notifyValueChanged.
1875 setAutofilledIfValuesIs(view, value);
1876
1877 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001878 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001879 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001880
Felipe Leme49f08ed2018-03-26 16:18:45 -07001881 if (failedIds != null) {
1882 if (sVerbose) {
1883 Log.v(TAG, "autofill(): total failed views: " + failedIds);
1884 }
1885 try {
1886 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
1887 } catch (RemoteException e) {
1888 // In theory, we could ignore this error since it's not a big deal, but
1889 // in reality, we rather crash the app anyways, as the failure could be
1890 // a consequence of something going wrong on the server side...
1891 e.rethrowFromSystemServer();
1892 }
1893 }
1894
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001895 if (virtualValues != null) {
1896 for (int i = 0; i < virtualValues.size(); i++) {
1897 final View parent = virtualValues.keyAt(i);
1898 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1899 parent.autofill(childrenValues);
1900 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07001901 // TODO: we should provide a callback so the parent can call failures; something
1902 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001903 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001904 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001905
Felipe Leme11166522018-05-07 10:18:47 -07001906 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07001907 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07001908 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001909 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001910 }
1911
Felipe Leme11166522018-05-07 10:18:47 -07001912 private LogMaker newLog(int category) {
Felipe Leme212b1612018-05-22 14:56:15 -07001913 final LogMaker log = new LogMaker(category)
Felipe Lemece3ae872018-05-24 10:41:48 -07001914 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
1915
1916 if (isCompatibilityModeEnabledLocked()) {
1917 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
1918 }
Felipe Leme212b1612018-05-22 14:56:15 -07001919 final AutofillClient client = getClient();
1920 if (client == null) {
1921 // Client should never be null here, but it doesn't hurt to check...
1922 log.setPackageName(mContext.getPackageName());
1923 } else {
1924 log.setComponentName(client.autofillClientGetComponentName());
1925 }
1926 return log;
Felipe Leme11166522018-05-07 10:18:47 -07001927 }
1928
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001929 /**
1930 * Set the tracked views.
1931 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001932 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001933 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001934 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001935 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001936 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001937 */
Felipe Leme27e20222017-05-18 15:24:11 -07001938 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001939 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1940 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001941 synchronized (mLock) {
1942 if (mEnabled && mSessionId == sessionId) {
1943 if (saveOnAllViewsInvisible) {
1944 mTrackedViews = new TrackedViews(trackedIds);
1945 } else {
1946 mTrackedViews = null;
1947 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001948 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001949 if (fillableIds != null) {
1950 if (mFillableIds == null) {
1951 mFillableIds = new ArraySet<>(fillableIds.length);
1952 }
1953 for (AutofillId id : fillableIds) {
1954 mFillableIds.add(id);
1955 }
1956 if (sVerbose) {
1957 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1958 + ", mFillableIds" + mFillableIds);
1959 }
1960 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001961
1962 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1963 // Turn off trigger on previous view id.
1964 setNotifyOnClickLocked(mSaveTriggerId, false);
1965 }
1966
1967 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1968 // Turn on trigger on new view id.
1969 mSaveTriggerId = saveTriggerId;
1970 setNotifyOnClickLocked(mSaveTriggerId, true);
1971 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001972 }
1973 }
1974 }
1975
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001976 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1977 final View view = findView(id);
1978 if (view == null) {
1979 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1980 return;
1981 }
1982 view.setNotifyAutofillManagerOnClick(notify);
1983 }
1984
Felipe Lemec24a56a2017-08-03 14:27:57 -07001985 private void setSaveUiState(int sessionId, boolean shown) {
1986 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1987 synchronized (mLock) {
1988 if (mSessionId != NO_SESSION) {
1989 // Race condition: app triggered a new session after the previous session was
1990 // finished but before server called setSaveUiState() - need to cancel the new
1991 // session to avoid further inconsistent behavior.
1992 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1993 + ") called on existing session " + mSessionId + "; cancelling it");
1994 cancelSessionLocked();
1995 }
1996 if (shown) {
1997 mSessionId = sessionId;
1998 mState = STATE_SHOWING_SAVE_UI;
1999 } else {
2000 mSessionId = NO_SESSION;
2001 mState = STATE_UNKNOWN;
2002 }
2003 }
2004 }
2005
Felipe Leme650f7ab2017-09-19 13:08:24 -07002006 /**
2007 * Marks the state of the session as finished.
2008 *
2009 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002010 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2011 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
2012 * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
2013 * disabled further autofill requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07002014 */
2015 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002016 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002017 if (sVerbose) {
2018 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
2019 + getStateAsString(newState));
2020 }
2021 if (newState == STATE_UNKNOWN_COMPAT_MODE) {
2022 resetSessionLocked(/* resetEnteredIds= */ true);
2023 mState = STATE_UNKNOWN;
2024 } else {
2025 resetSessionLocked(/* resetEnteredIds= */ false);
2026 mState = newState;
2027 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002028 }
2029 }
2030
Dake Gub0fa3782018-02-26 12:25:14 -08002031 /** @hide */
2032 public void requestHideFillUi() {
2033 requestHideFillUi(mIdShownFillUi, true);
2034 }
2035
2036 private void requestHideFillUi(AutofillId id, boolean force) {
2037 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002038 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002039 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002040 if (force) {
2041 // When user taps outside autofill window, force to close fill ui even id does
2042 // not match.
2043 AutofillClient client = getClient();
2044 if (client != null) {
2045 client.autofillClientRequestHideFillUi();
2046 }
2047 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002048 return;
2049 }
Felipe Leme27e20222017-05-18 15:24:11 -07002050 requestHideFillUi(id, anchor);
2051 }
2052
2053 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002054
2055 AutofillCallback callback = null;
2056 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002057 // We do not check the session id for two reasons:
2058 // 1. If local and remote session id are off sync the UI would be stuck shown
2059 // 2. There is a race between the user state being destroyed due the fill
2060 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002061 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002062 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002063 if (client.autofillClientRequestHideFillUi()) {
2064 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002065 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002066 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002067 }
2068 }
2069
2070 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002071 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002072 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002073 AutofillCallback.EVENT_INPUT_HIDDEN);
2074 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002075 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002076 }
2077 }
2078 }
2079
Felipe Leme17292d12017-10-24 14:03:10 -07002080 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002081 if (sVerbose) {
2082 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002083 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002084 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002085 final View anchor = findView(id);
2086 if (anchor == null) {
2087 return;
2088 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002089
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002090 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002091 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002092 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002093 callback = mCallback;
2094 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002095 }
2096
2097 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002098 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002099 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002100 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2101 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002102 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002103 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002104 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002105
Felipe Leme17292d12017-10-24 14:03:10 -07002106 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002107 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07002108 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07002109 }
2110 }
2111
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002112 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002113 * Find a single view by its id.
2114 *
2115 * @param autofillId The autofill id of the view
2116 *
2117 * @return The view or {@code null} if view was not found
2118 */
2119 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002120 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002121 if (client != null) {
2122 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002123 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002124 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002125 }
2126
Felipe Lemebb810922017-04-25 15:54:06 -07002127 /** @hide */
2128 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002129 return mService != null;
2130 }
2131
Felipe Lemec24a56a2017-08-03 14:27:57 -07002132 /** @hide */
2133 public void onPendingSaveUi(int operation, IBinder token) {
2134 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2135
2136 synchronized (mLock) {
2137 try {
2138 mService.onPendingSaveUi(operation, token);
2139 } catch (RemoteException e) {
2140 e.rethrowFromSystemServer();
2141 }
2142 }
2143 }
2144
2145 /** @hide */
2146 public void dump(String outerPrefix, PrintWriter pw) {
2147 pw.print(outerPrefix); pw.println("AutofillManager:");
2148 final String pfx = outerPrefix + " ";
2149 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002150 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002151 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme42260332018-08-15 08:44:12 -07002152 final AutofillClient client = getClient();
2153 if (client != null) {
2154 pw.print(pfx); pw.print("client: "); pw.print(client);
2155 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2156 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002157 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
2158 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2159 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002160 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002161 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
2162 pw.print(pfx); pw.print("tracked views: ");
2163 if (mTrackedViews == null) {
2164 pw.println("null");
2165 } else {
2166 final String pfx2 = pfx + " ";
2167 pw.println();
2168 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2169 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2170 }
2171 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002172 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002173 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2174 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme42260332018-08-15 08:44:12 -07002175 pw.print(pfx); pw.print("compat mode enabled: ");
2176 synchronized (mLock) {
2177 if (mCompatibilityBridge != null) {
2178 final String pfx2 = pfx + " ";
2179 pw.println("true");
2180 pw.print(pfx2); pw.print("windowId: ");
2181 pw.println(mCompatibilityBridge.mFocusedWindowId);
2182 pw.print(pfx2); pw.print("nodeId: ");
2183 pw.println(mCompatibilityBridge.mFocusedNodeId);
2184 pw.print(pfx2); pw.print("virtualId: ");
2185 pw.println(AccessibilityNodeInfo
2186 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2187 pw.print(pfx2); pw.print("focusedBounds: ");
2188 pw.println(mCompatibilityBridge.mFocusedBounds);
2189 } else {
2190 pw.println("false");
2191 }
2192 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002193 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2194 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002195 }
2196
Andreas Gampe3f24e692018-02-05 13:24:28 -08002197 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002198 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002199 return getStateAsString(mState);
2200 }
2201
2202 @NonNull
2203 private static String getStateAsString(int state) {
2204 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002205 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002206 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002207 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002208 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002209 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002210 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002211 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002212 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002213 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002214 return "DISABLED_BY_SERVICE";
2215 case STATE_UNKNOWN_COMPAT_MODE:
2216 return "UNKNOWN_COMPAT_MODE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002217 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002218 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002219 }
2220 }
2221
Andreas Gampe3f24e692018-02-05 13:24:28 -08002222 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002223 private boolean isActiveLocked() {
2224 return mState == STATE_ACTIVE;
2225 }
2226
Andreas Gampe3f24e692018-02-05 13:24:28 -08002227 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002228 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002229 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002230 }
2231
Andreas Gampe3f24e692018-02-05 13:24:28 -08002232 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002233 private boolean isFinishedLocked() {
2234 return mState == STATE_FINISHED;
2235 }
2236
Felipe Leme9876a6f2017-05-30 15:47:28 -07002237 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002238 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002239 if (client == null) {
2240 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2241 return;
2242 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002243 client.autofillClientRunOnUiThread(runnable);
2244 }
2245
2246 /**
2247 * Implementation of the accessibility based compatibility.
2248 */
2249 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2250 @GuardedBy("mLock")
2251 private final Rect mFocusedBounds = new Rect();
2252 @GuardedBy("mLock")
2253 private final Rect mTempBounds = new Rect();
2254
2255 @GuardedBy("mLock")
2256 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2257 @GuardedBy("mLock")
2258 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2259
2260 // Need to report a fake service in case a11y clients check the service list
2261 @NonNull
2262 @GuardedBy("mLock")
2263 AccessibilityServiceInfo mCompatServiceInfo;
2264
2265 CompatibilityBridge() {
2266 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2267 am.setAccessibilityPolicy(this);
2268 }
2269
2270 private AccessibilityServiceInfo getCompatServiceInfo() {
2271 synchronized (mLock) {
2272 if (mCompatServiceInfo != null) {
2273 return mCompatServiceInfo;
2274 }
2275 final Intent intent = new Intent();
2276 intent.setComponent(new ComponentName("android",
2277 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2278 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2279 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2280 try {
2281 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2282 } catch (XmlPullParserException | IOException e) {
2283 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2284 throw new IllegalStateException("Cannot find compat autofill service");
2285 }
2286 return mCompatServiceInfo;
2287 }
2288 }
2289
2290 @Override
2291 public boolean isEnabled(boolean accessibilityEnabled) {
2292 return true;
2293 }
2294
2295 @Override
2296 public int getRelevantEventTypes(int relevantEventTypes) {
2297 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2298 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002299 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002300 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2301 }
2302
2303 @Override
2304 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2305 List<AccessibilityServiceInfo> installedServices) {
2306 if (installedServices == null) {
2307 installedServices = new ArrayList<>();
2308 }
2309 installedServices.add(getCompatServiceInfo());
2310 return installedServices;
2311 }
2312
2313 @Override
2314 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2315 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2316 if (enabledService == null) {
2317 enabledService = new ArrayList<>();
2318 }
2319 enabledService.add(getCompatServiceInfo());
2320 return enabledService;
2321 }
2322
2323 @Override
2324 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2325 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Lemea9aacf92018-08-13 18:15:32 -07002326 final int type = event.getEventType();
2327 if (sVerbose) {
2328 // NOTE: this is waaay spammy, but that's life.
2329 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme34384212018-08-16 10:49:03 -07002330 + "): virtualId="
2331 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2332 + ", client=" + getClient());
Felipe Lemea9aacf92018-08-13 18:15:32 -07002333 }
2334 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002335 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2336 synchronized (mLock) {
2337 if (mFocusedWindowId == event.getWindowId()
2338 && mFocusedNodeId == event.getSourceNodeId()) {
2339 return event;
2340 }
2341 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2342 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2343 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2344 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2345 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2346 mFocusedBounds.set(0, 0, 0, 0);
2347 }
2348 final int windowId = event.getWindowId();
2349 final long nodeId = event.getSourceNodeId();
2350 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2351 mFocusedWindowId = windowId;
2352 mFocusedNodeId = nodeId;
2353 }
2354 }
2355 } break;
2356
2357 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2358 synchronized (mLock) {
2359 if (mFocusedWindowId == event.getWindowId()
2360 && mFocusedNodeId == event.getSourceNodeId()) {
2361 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2362 }
2363 }
2364 } break;
2365
Felipe Leme54cc6832018-03-06 12:54:31 -08002366 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2367 synchronized (mLock) {
2368 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2369 }
2370 } break;
2371
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002372 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2373 final AutofillClient client = getClient();
2374 if (client != null) {
2375 synchronized (mLock) {
2376 if (client.autofillClientIsFillUiShowing()) {
2377 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2378 }
2379 updateTrackedViewsLocked();
2380 }
2381 }
2382 } break;
2383 }
2384
2385 return accessibilityEnabled ? event : null;
2386 }
2387
2388 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2389 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2390 if (!isVirtualNode(virtualId)) {
2391 return false;
2392 }
2393 final View view = findViewByAccessibilityId(windowId, nodeId);
2394 if (view == null) {
2395 return false;
2396 }
2397 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2398 if (node == null) {
2399 return false;
2400 }
2401 if (!node.isEditable()) {
2402 return false;
2403 }
2404 final Rect newBounds = mTempBounds;
2405 node.getBoundsInScreen(newBounds);
2406 if (newBounds.equals(focusedBounds)) {
2407 return false;
2408 }
2409 focusedBounds.set(newBounds);
2410 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2411 return true;
2412 }
2413
2414 private void notifyViewExited(int windowId, long nodeId) {
2415 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2416 if (!isVirtualNode(virtualId)) {
2417 return;
2418 }
2419 final View view = findViewByAccessibilityId(windowId, nodeId);
2420 if (view == null) {
2421 return;
2422 }
2423 AutofillManager.this.notifyViewExited(view, virtualId);
2424 }
2425
2426 private void notifyValueChanged(int windowId, long nodeId) {
2427 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2428 if (!isVirtualNode(virtualId)) {
2429 return;
2430 }
2431 final View view = findViewByAccessibilityId(windowId, nodeId);
2432 if (view == null) {
2433 return;
2434 }
2435 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2436 if (node == null) {
2437 return;
2438 }
2439 AutofillManager.this.notifyValueChanged(view, virtualId,
2440 AutofillValue.forText(node.getText()));
2441 }
2442
Felipe Leme54cc6832018-03-06 12:54:31 -08002443 private void notifyViewClicked(int windowId, long nodeId) {
2444 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2445 if (!isVirtualNode(virtualId)) {
2446 return;
2447 }
2448 final View view = findViewByAccessibilityId(windowId, nodeId);
2449 if (view == null) {
2450 return;
2451 }
2452 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2453 if (node == null) {
2454 return;
2455 }
2456 AutofillManager.this.notifyViewClicked(view, virtualId);
2457 }
2458
Andreas Gampe3f24e692018-02-05 13:24:28 -08002459 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002460 private void updateTrackedViewsLocked() {
2461 if (mTrackedViews != null) {
2462 mTrackedViews.onVisibleForAutofillChangedLocked();
2463 }
2464 }
2465
2466 private View findViewByAccessibilityId(int windowId, long nodeId) {
2467 final AutofillClient client = getClient();
2468 if (client == null) {
2469 return null;
2470 }
2471 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2472 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2473 }
2474
2475 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2476 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2477 if (provider == null) {
2478 return null;
2479 }
2480 return provider.createAccessibilityNodeInfo(virtualId);
2481 }
2482
2483 private boolean isVirtualNode(int nodeId) {
2484 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2485 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2486 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002487 }
2488
Felipe Lemee6010f22017-03-03 11:19:51 -08002489 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002490 * View tracking information. Once all tracked views become invisible the session is finished.
2491 */
2492 private class TrackedViews {
2493 /** Visible tracked views */
2494 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2495
2496 /** Invisible tracked views */
2497 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2498
2499 /**
2500 * Check if set is null or value is in set.
2501 *
2502 * @param set The set or null (== empty set)
2503 * @param value The value that might be in the set
2504 *
2505 * @return {@code true} iff set is not empty and value is in set
2506 */
Felipe Leme27e20222017-05-18 15:24:11 -07002507 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002508 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2509 return set != null && set.contains(value);
2510 }
2511
2512 /**
2513 * Add a value to a set. If set is null, create a new set.
2514 *
2515 * @param set The set or null (== empty set)
2516 * @param valueToAdd The value to add
2517 *
2518 * @return The set including the new value. If set was {@code null}, a set containing only
2519 * the new value.
2520 */
Felipe Leme27e20222017-05-18 15:24:11 -07002521 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002522 @NonNull
2523 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2524 if (set == null) {
2525 set = new ArraySet<>(1);
2526 }
2527
2528 set.add(valueToAdd);
2529
2530 return set;
2531 }
2532
2533 /**
2534 * Remove a value from a set.
2535 *
2536 * @param set The set or null (== empty set)
2537 * @param valueToRemove The value to remove
2538 *
2539 * @return The set without the removed value. {@code null} if set was null, or is empty
2540 * after removal.
2541 */
Felipe Leme27e20222017-05-18 15:24:11 -07002542 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002543 @Nullable
2544 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2545 if (set == null) {
2546 return null;
2547 }
2548
2549 set.remove(valueToRemove);
2550
2551 if (set.isEmpty()) {
2552 return null;
2553 }
2554
2555 return set;
2556 }
2557
2558 /**
2559 * Set the tracked views.
2560 *
2561 * @param trackedIds The views to be tracked
2562 */
Felipe Leme27e20222017-05-18 15:24:11 -07002563 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002564 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002565 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002566 final boolean[] isVisible;
2567
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002568 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002569 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002570 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002571 } else {
2572 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002573 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002574 }
2575
Felipe Leme27e20222017-05-18 15:24:11 -07002576 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002577 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002578 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002579
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002580 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002581 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002582 } else {
2583 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2584 }
2585 }
2586 }
2587
Felipe Leme9f9ee252017-04-27 13:56:22 -07002588 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002589 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002590 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2591 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2592 }
2593
2594 if (mVisibleTrackedIds == null) {
2595 finishSessionLocked();
2596 }
2597 }
2598
2599 /**
2600 * Called when a {@link View view's} visibility changes.
2601 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002602 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002603 * @param isVisible visible if the view is visible in the view hierarchy.
2604 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002605 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002606 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002607 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002608 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002609 + isVisible);
2610 }
2611
Dake Gu67decfa2017-12-27 11:48:08 -08002612 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002613 if (isVisible) {
2614 if (isInSet(mInvisibleTrackedIds, id)) {
2615 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2616 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2617 }
2618 } else {
2619 if (isInSet(mVisibleTrackedIds, id)) {
2620 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2621 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2622 }
2623 }
2624 }
2625
2626 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002627 if (sVerbose) {
2628 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2629 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002630 finishSessionLocked();
2631 }
2632 }
2633
2634 /**
2635 * Called once the client becomes visible.
2636 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002637 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002638 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002639 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002640 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002641 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002642 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002643 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002644 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2645 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2646 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07002647 if (sVerbose) {
2648 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
2649 + " vis=" + mVisibleTrackedIds);
2650 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002651 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002652 final ArrayList<AutofillId> orderedInvisibleIds =
2653 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002654 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2655 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002656
2657 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2658 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2659 final AutofillId id = orderedInvisibleIds.get(i);
2660 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002661 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2662
Felipe Leme9f9ee252017-04-27 13:56:22 -07002663 if (sDebug) {
2664 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002665 }
2666 } else {
2667 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2668 }
2669 }
2670 }
2671
2672 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002673 final ArrayList<AutofillId> orderedVisibleIds =
2674 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002675 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2676 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002677
2678 final int numVisibleTrackedIds = orderedVisibleIds.size();
2679 for (int i = 0; i < numVisibleTrackedIds; i++) {
2680 final AutofillId id = orderedVisibleIds.get(i);
2681
2682 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002683 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2684 } else {
2685 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2686
Felipe Leme9f9ee252017-04-27 13:56:22 -07002687 if (sDebug) {
2688 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002689 }
2690 }
2691 }
2692 }
2693
2694 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2695 mVisibleTrackedIds = updatedVisibleTrackedIds;
2696 }
2697
2698 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002699 if (sVerbose) {
2700 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
2701 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002702 finishSessionLocked();
2703 }
2704 }
2705 }
2706
2707 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002708 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002709 *
2710 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002711 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002712 */
2713 public abstract static class AutofillCallback {
2714
2715 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002716 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2717 EVENT_INPUT_SHOWN,
2718 EVENT_INPUT_HIDDEN,
2719 EVENT_INPUT_UNAVAILABLE
2720 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002721 @Retention(RetentionPolicy.SOURCE)
2722 public @interface AutofillEventType {}
2723
2724 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002725 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002726 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002727 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002728 * should be hidden upon receiving this event.
2729 */
2730 public static final int EVENT_INPUT_SHOWN = 1;
2731
2732 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002733 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002734 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002735 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002736 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2737 */
2738 public static final int EVENT_INPUT_HIDDEN = 2;
2739
2740 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002741 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002742 * autofill is not available.
2743 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002744 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002745 * to avoid flickering, it could shown it upon receiving this event.
2746 */
2747 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2748
2749 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002750 * Called after a change in the autofill state associated with a view.
2751 *
2752 * @param view view associated with the change.
2753 *
2754 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2755 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002756 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2757 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002758
2759 /**
2760 * Called after a change in the autofill state associated with a virtual view.
2761 *
2762 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002763 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002764 *
2765 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2766 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002767 public void onAutofillEvent(@NonNull View view, int virtualId,
2768 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002769 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002770 }
2771
Felipe Leme640f30a2017-03-06 15:44:06 -08002772 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2773 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002774
Felipe Leme640f30a2017-03-06 15:44:06 -08002775 AutofillManagerClient(AutofillManager autofillManager) {
2776 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002777 }
2778
2779 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002780 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002781 final AutofillManager afm = mAfm.get();
2782 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002783 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002784 }
2785 }
2786
2787 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002788 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002789 final AutofillManager afm = mAfm.get();
2790 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002791 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002792 }
2793 }
2794
2795 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002796 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2797 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002798 final AutofillManager afm = mAfm.get();
2799 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002800 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002801 }
2802 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002803
2804 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002805 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2806 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002807 final AutofillManager afm = mAfm.get();
2808 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002809 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2810 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002811 }
2812 }
2813
2814 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002815 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002816 final AutofillManager afm = mAfm.get();
2817 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002818 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07002819 }
2820 }
2821
2822 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002823 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002824 final AutofillManager afm = mAfm.get();
2825 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002826 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002827 }
2828 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002829
2830 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08002831 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
2832 final AutofillManager afm = mAfm.get();
2833 if (afm != null) {
2834 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
2835 }
2836 }
2837
2838 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002839 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002840 final AutofillManager afm = mAfm.get();
2841 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002842 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002843 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002844 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002845 } catch (IntentSender.SendIntentException e) {
2846 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2847 }
2848 });
2849 }
2850 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002851
2852 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002853 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002854 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2855 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002856 final AutofillManager afm = mAfm.get();
2857 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002858 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2859 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002860 }
2861 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002862
2863 @Override
2864 public void setSaveUiState(int sessionId, boolean shown) {
2865 final AutofillManager afm = mAfm.get();
2866 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002867 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2868 }
2869 }
2870
2871 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002872 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002873 final AutofillManager afm = mAfm.get();
2874 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002875 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002876 }
2877 }
Svet Ganov782043c2017-02-11 00:52:02 +00002878 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002879}