blob: e2022de80abeab7d9765f5c263cb586b24929059 [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
Felipe Leme73fedac2017-05-12 09:52:07 -070019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
Felipe Leme9f9ee252017-04-27 13:56:22 -070020import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
Felipe Lemed633f072017-02-14 10:17:17 -080022
Felipe Lemee6010f22017-03-03 11:19:51 -080023import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080024import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080025import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.SystemService;
Felipe Leme3461d3c2017-01-19 08:54:55 -080027import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000028import android.content.Intent;
29import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080030import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070031import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070033import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000034import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070036import android.service.autofill.AutofillService;
37import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070038import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070039import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070041import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080042import android.view.View;
43
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070044import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070045import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070046import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme4753bb02017-03-22 20:24:00 -070047
Felipe Lemec24a56a2017-08-03 14:27:57 -070048import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080049import java.lang.annotation.Retention;
50import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000051import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070052import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000053import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070054import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000055
Felipe Leme3461d3c2017-01-19 08:54:55 -080056/**
Felipe Leme744976e2017-07-31 11:34:14 -070057 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
58 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070059 *
Felipe Leme744976e2017-07-31 11:34:14 -070060 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
61 * activity context; the autofill context is created when one of the following methods is called for
62 * the first time in an activity context, and the current user has an enabled autofill service:
63 *
64 * <ul>
65 * <li>{@link #notifyViewEntered(View)}
66 * <li>{@link #notifyViewEntered(View, int, Rect)}
67 * <li>{@link #requestAutofill(View)}
68 * </ul>
69 *
70 * <p>Tipically, the context is automatically created when the first view of the activity is
71 * focused because {@code View.onFocusChanged()} indirectly calls
72 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
73 * explicitly create it (for example, a custom view developer could offer a contextual menu action
74 * in a text-field view to let users manually request autofill).
75 *
76 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
77 * that represents the view hierarchy by calling
78 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
79 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
80 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
81 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
82 * the hierarchy.
83 *
84 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
85 * parses it looking for views that can be autofilled. If the service finds such views, it returns
86 * a data structure to the Android System containing the following optional info:
87 *
88 * <ul>
89 * <li>Datasets used to autofill subsets of views in the activity.
90 * <li>Id of views that the service can save their values for future autofilling.
91 * </ul>
92 *
93 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
94 * UI affordance associated with the view, when the view is focused on and is part of a dataset.
95 * The application can be notified when the affordance is shown by registering an
96 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
97 * selects a dataset from the affordance, all views present in the dataset are autofilled, through
98 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
99 *
100 * <p>When the service returns ids of savable views, the Android System keeps track of changes
101 * made to these views, so they can be used to determine if the autofill save UI is shown later.
102 *
103 * <p>The context is then finished when one of the following occurs:
104 *
105 * <ul>
106 * <li>{@link #commit()} is called or all savable views are gone.
107 * <li>{@link #cancel()} is called.
108 * </ul>
109 *
110 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
111 * shows a save UI affordance if the value of savable views have changed. If the user selects the
112 * option to Save, the current value of the views is then sent to the autofill service.
113 *
114 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800115 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600116@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800117public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800118
Felipe Leme640f30a2017-03-06 15:44:06 -0800119 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800120
Svet Ganov782043c2017-02-11 00:52:02 +0000121 /**
122 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700123 *
Svet Ganov782043c2017-02-11 00:52:02 +0000124 * <p>
125 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000126 */
127 public static final String EXTRA_ASSIST_STRUCTURE =
128 "android.view.autofill.extra.ASSIST_STRUCTURE";
129
130 /**
131 * Intent extra: The result of an authentication operation. It is
132 * either a fully populated {@link android.service.autofill.FillResponse}
133 * or a fully populated {@link android.service.autofill.Dataset} if
134 * a response or a dataset is being authenticated respectively.
135 *
136 * <p>
137 * Type: {@link android.service.autofill.FillResponse} or a
138 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000139 */
140 public static final String EXTRA_AUTHENTICATION_RESULT =
141 "android.view.autofill.extra.AUTHENTICATION_RESULT";
142
Felipe Leme7320ca92017-03-29 15:09:54 -0700143 /**
144 * Intent extra: The optional extras provided by the
145 * {@link android.service.autofill.AutofillService}.
146 *
147 * <p>For example, when the service responds to a {@link
148 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
149 * a {@code FillResponse} that requires authentication, the Intent that launches the
150 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700151 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700152 *
Felipe Lemea9372382017-10-09 14:42:36 -0700153 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
154 * can also add this bundle to the {@link Intent} set as the
155 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
156 * so the bundle can be recovered later on
157 * {@link android.service.autofill.SaveRequest#getClientState()}.
158 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700159 * <p>
160 * Type: {@link android.os.Bundle}
161 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700162 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600163 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700164
Felipe Lemec24a56a2017-08-03 14:27:57 -0700165
166 /** @hide */
167 public static final String EXTRA_RESTORE_SESSION_TOKEN =
168 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
169
170 private static final String SESSION_ID_TAG = "android:sessionId";
171 private static final String STATE_TAG = "android:state";
172 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
173
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700174
Felipe Leme0aa4c502017-04-26 12:36:01 -0700175 /** @hide */ public static final int ACTION_START_SESSION = 1;
176 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
177 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
178 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800179
Felipe Leme9f9ee252017-04-27 13:56:22 -0700180
181 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
182 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
183 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
184
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700185 /** Which bits in an authentication id are used for the dataset id */
186 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
187 /** How many bits in an authentication id are used for the dataset id */
188 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
189 /** @hide The index for an undefined data set */
190 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
191
192 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700193 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
194 *
195 * @hide
196 */
197 public static final int PENDING_UI_OPERATION_CANCEL = 1;
198
199 /**
200 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
201 *
202 * @hide
203 */
204 public static final int PENDING_UI_OPERATION_RESTORE = 2;
205
206 /**
207 * Initial state of the autofill context, set when there is no session (i.e., when
208 * {@link #mSessionId} is {@link #NO_SESSION}).
209 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700210 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
211 * the server.
212 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700213 * @hide
214 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700215 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700216
217 /**
218 * State where the autofill context hasn't been {@link #commit() finished} nor
219 * {@link #cancel() canceled} yet.
220 *
221 * @hide
222 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700223 public static final int STATE_ACTIVE = 1;
224
225 /**
226 * State where the autofill context was finished by the server because the autofill
227 * service could not autofill the page.
228 *
229 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
230 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
231 *
232 * @hide
233 */
234 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700235
236 /**
237 * State where the autofill context has been {@link #commit() finished} but the server still has
238 * a session because the Save UI hasn't been dismissed yet.
239 *
240 * @hide
241 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700242 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700243
244 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700245 * Makes an authentication id from a request id and a dataset id.
246 *
247 * @param requestId The request id.
248 * @param datasetId The dataset id.
249 * @return The authentication id.
250 * @hide
251 */
252 public static int makeAuthenticationId(int requestId, int datasetId) {
253 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
254 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
255 }
256
257 /**
258 * Gets the request id from an authentication id.
259 *
260 * @param authRequestId The authentication id.
261 * @return The request id.
262 * @hide
263 */
264 public static int getRequestIdFromAuthenticationId(int authRequestId) {
265 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
266 }
267
268 /**
269 * Gets the dataset id from an authentication id.
270 *
271 * @param authRequestId The authentication id.
272 * @return The dataset id.
273 * @hide
274 */
275 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
276 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
277 }
278
Felipe Leme4753bb02017-03-22 20:24:00 -0700279 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000280
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700281 /**
282 * There is currently no session running.
283 * {@hide}
284 */
285 public static final int NO_SESSION = Integer.MIN_VALUE;
286
Svet Ganov782043c2017-02-11 00:52:02 +0000287 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700288
289 private final Object mLock = new Object();
290
291 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000292 private IAutoFillManagerClient mServiceClient;
293
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700294 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800295 private AutofillCallback mCallback;
296
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700297 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000298
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700299 @GuardedBy("mLock")
300 private int mSessionId = NO_SESSION;
301
302 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700303 private int mState = STATE_UNKNOWN;
304
305 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000306 private boolean mEnabled;
307
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700308 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700309 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700310 @Nullable private ParcelableMap mLastAutofilledData;
311
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700312 /** If view tracking is enabled, contains the tracking state */
313 @GuardedBy("mLock")
314 @Nullable private TrackedViews mTrackedViews;
315
Felipe Leme27e20222017-05-18 15:24:11 -0700316 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
317 @GuardedBy("mLock")
318 @Nullable private ArraySet<AutofillId> mFillableIds;
319
Svet Ganov782043c2017-02-11 00:52:02 +0000320 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800321 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000322 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000323 * Asks the client to start an authentication flow.
324 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700325 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000326 * @param intent The authentication intent.
327 * @param fillInIntent The authentication fill-in intent.
328 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700329 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
330 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800331
332 /**
333 * Tells the client this manager has state to be reset.
334 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700335 void autofillCallbackResetableStateAvailable();
336
337 /**
338 * Request showing the autofill UI.
339 *
340 * @param anchor The real view the UI needs to anchor to.
341 * @param width The width of the fill UI content.
342 * @param height The height of the fill UI content.
343 * @param virtualBounds The bounds of the virtual decendant of the anchor.
344 * @param presenter The presenter that controls the fill UI window.
345 * @return Whether the UI was shown.
346 */
347 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
348 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
349
350 /**
351 * Request hiding the autofill UI.
352 *
353 * @return Whether the UI was hidden.
354 */
355 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700356
357 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700358 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700359 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700360 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700361 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700362 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700363
364 /**
365 * Checks is the client is currently visible as understood by autofill.
366 *
367 * @return {@code true} if the client is currently visible
368 */
369 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700370
371 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700372 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700373 *
Phil Weaver846cda932017-06-15 10:10:06 -0700374 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700375 *
Felipe Leme27e20222017-05-18 15:24:11 -0700376 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700377 */
Phil Weaver846cda932017-06-15 10:10:06 -0700378 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700379
380 /**
381 * Finds a view by traversing the hierarchies of the client.
382 *
Phil Weaver846cda932017-06-15 10:10:06 -0700383 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700384 *
385 * @return The view, or {@code null} if not found
386 */
Phil Weaver846cda932017-06-15 10:10:06 -0700387 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700388
389 /**
390 * Runs the specified action on the UI thread.
391 */
392 void runOnUiThread(Runnable action);
Svet Ganov782043c2017-02-11 00:52:02 +0000393 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800394
395 /**
396 * @hide
397 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800398 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800399 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800400 mService = service;
401 }
402
403 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700404 * Restore state after activity lifecycle
405 *
406 * @param savedInstanceState The state to be restored
407 *
408 * {@hide}
409 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700410 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700411 if (!hasAutofillFeature()) {
412 return;
413 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700414 synchronized (mLock) {
415 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
416
Felipe Lemec24a56a2017-08-03 14:27:57 -0700417 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700418 Log.w(TAG, "New session was started before onCreate()");
419 return;
420 }
421
422 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700423 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700424
425 if (mSessionId != NO_SESSION) {
426 ensureServiceClientAddedIfNeededLocked();
427
428 final AutofillClient client = getClientLocked();
429 if (client != null) {
430 try {
431 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
432 mContext.getActivityToken(), mServiceClient.asBinder());
433
434 if (!sessionWasRestored) {
435 Log.w(TAG, "Session " + mSessionId + " could not be restored");
436 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700437 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700438 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700439 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700440 Log.d(TAG, "session " + mSessionId + " was restored");
441 }
442
443 client.autofillCallbackResetableStateAvailable();
444 }
445 } catch (RemoteException e) {
446 Log.e(TAG, "Could not figure out if there was an autofill session", e);
447 }
448 }
449 }
450 }
451 }
452
453 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700454 * Called once the client becomes visible.
455 *
456 * @see AutofillClient#isVisibleForAutofill()
457 *
458 * {@hide}
459 */
460 public void onVisibleForAutofill() {
461 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700462 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700463 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700464 }
465 }
466 }
467
468 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700469 * Save state before activity lifecycle
470 *
471 * @param outState Place to store the state
472 *
473 * {@hide}
474 */
475 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700476 if (!hasAutofillFeature()) {
477 return;
478 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700479 synchronized (mLock) {
480 if (mSessionId != NO_SESSION) {
481 outState.putInt(SESSION_ID_TAG, mSessionId);
482 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700483 if (mState != STATE_UNKNOWN) {
484 outState.putInt(STATE_TAG, mState);
485 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700486 if (mLastAutofilledData != null) {
487 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
488 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700489 }
490 }
491
492 /**
493 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700494 *
495 * <p>Typically used to determine whether the option to explicitly request autofill should
496 * be offered - see {@link #requestAutofill(View)}.
497 *
498 * @return whether autofill is enabled for the current user.
499 */
500 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700501 if (!hasAutofillFeature()) {
502 return false;
503 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700504 synchronized (mLock) {
505 ensureServiceClientAddedIfNeededLocked();
506 return mEnabled;
507 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700508 }
509
510 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700511 * Should always be called from {@link AutofillService#getFillEventHistory()}.
512 *
513 * @hide
514 */
515 @Nullable public FillEventHistory getFillEventHistory() {
516 try {
517 return mService.getFillEventHistory();
518 } catch (RemoteException e) {
519 e.rethrowFromSystemServer();
520 return null;
521 }
522 }
523
524 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700525 * Explicitly requests a new autofill context.
526 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700527 * <p>Normally, the autofill context is automatically started if necessary when
528 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
529 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
530 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700531 *
532 * @param view view requesting the new autofill context.
533 */
534 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700535 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700536 }
537
538 /**
539 * Explicitly requests a new autofill context for virtual views.
540 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700541 * <p>Normally, the autofill context is automatically started if necessary when
542 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
543 * cases where it must be explicitly started. For example, when the virtual view offers an
544 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700545 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700546 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
547 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
548 * the absolute bounds could be calculated by:
549 *
550 * <pre class="prettyprint">
551 * int offset[] = new int[2];
552 * getLocationOnScreen(offset);
553 * Rect absBounds = new Rect(bounds.left + offset[0],
554 * bounds.top + offset[1],
555 * bounds.right + offset[0], bounds.bottom + offset[1]);
556 * </pre>
557 *
558 * @param view the virtual view parent.
559 * @param virtualId id identifying the virtual child inside the parent view.
560 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700561 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700562 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
563 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700564 }
565
Felipe Leme2ac463e2017-03-13 14:06:25 -0700566 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700567 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800568 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700569 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800570 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700571 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700572 notifyViewEntered(view, 0);
573 }
574
575 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700576 if (!hasAutofillFeature()) {
577 return;
578 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700579 AutofillCallback callback = null;
580 synchronized (mLock) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700581 if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
582 if (sVerbose) {
583 Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
584 + "): ignored on state " + getStateAsStringLocked());
585 }
586 return;
587 }
588
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700589 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000590
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700591 if (!mEnabled) {
592 if (mCallback != null) {
593 callback = mCallback;
594 }
595 } else {
596 final AutofillId id = getAutofillId(view);
597 final AutofillValue value = view.getAutofillValue();
598
Felipe Lemec24a56a2017-08-03 14:27:57 -0700599 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700600 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700601 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700602 } else {
603 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700604 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700605 }
Felipe Leme24aae152017-03-15 12:33:01 -0700606 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800607 }
608
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700609 if (callback != null) {
610 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000611 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800612 }
613
614 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700615 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800616 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700617 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800618 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700619 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700620 if (!hasAutofillFeature()) {
621 return;
622 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700623 synchronized (mLock) {
624 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800625
Felipe Lemec24a56a2017-08-03 14:27:57 -0700626 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700627 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800628
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700629 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700630 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700631 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800632 }
633 }
634
635 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700636 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700637 *
638 * @param view {@link View} that was exited.
639 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700640 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700641 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
642 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
643 }
644
645 /**
646 * Called when a virtual view's visibility changed.
647 *
648 * @param view {@link View} that was exited.
649 * @param virtualId id identifying the virtual child inside the parent view.
650 * @param isVisible visible if the view is visible in the view hierarchy.
651 */
652 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
653 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
654 }
655
656 /**
657 * Called when a view/virtual view's visibility changed.
658 *
659 * @param view {@link View} that was exited.
660 * @param virtualId id identifying the virtual child inside the parent view.
661 * @param isVisible visible if the view is visible in the view hierarchy.
662 * @param virtual Whether the view is virtual.
663 */
664 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
665 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700666 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700667 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700668 final AutofillId id = virtual ? getAutofillId(view, virtualId)
669 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700670 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700671 if (mFillableIds.contains(id)) {
672 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
673 requestHideFillUi(id, view);
674 }
675 }
676 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700677 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700678 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700679 }
680 }
681 }
682
683 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700684 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800685 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700686 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
687 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
688 * the absolute bounds could be calculated by:
689 *
690 * <pre class="prettyprint">
691 * int offset[] = new int[2];
692 * getLocationOnScreen(offset);
693 * Rect absBounds = new Rect(bounds.left + offset[0],
694 * bounds.top + offset[1],
695 * bounds.right + offset[0], bounds.bottom + offset[1]);
696 * </pre>
697 *
698 * @param view the virtual view parent.
699 * @param virtualId id identifying the virtual child inside the parent view.
700 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800701 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700702 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
703 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700704 }
705
Felipe Leme6dcec872017-05-25 11:24:23 -0700706 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700707 if (!hasAutofillFeature()) {
708 return;
709 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700710 AutofillCallback callback = null;
711 synchronized (mLock) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700712 if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
713 if (sVerbose) {
714 Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
715 + ", virtualId=" + virtualId
716 + "): ignored on state " + getStateAsStringLocked());
717 }
718 return;
719 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700720 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000721
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700722 if (!mEnabled) {
723 if (mCallback != null) {
724 callback = mCallback;
725 }
726 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700727 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700728
Felipe Lemec24a56a2017-08-03 14:27:57 -0700729 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700730 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700731 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700732 } else {
733 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700734 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700735 }
Felipe Leme24aae152017-03-15 12:33:01 -0700736 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800737 }
738
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700739 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700740 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700741 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800742 }
743 }
744
745 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700746 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800747 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700748 * @param view the virtual view parent.
749 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800750 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700751 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700752 if (!hasAutofillFeature()) {
753 return;
754 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700755 synchronized (mLock) {
756 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800757
Felipe Lemec24a56a2017-08-03 14:27:57 -0700758 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700759 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800760
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700761 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700762 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700763 }
Svet Ganov782043c2017-02-11 00:52:02 +0000764 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800765 }
766
767 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800768 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800769 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800770 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800771 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700772 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700773 if (!hasAutofillFeature()) {
774 return;
775 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700776 AutofillId id = null;
777 boolean valueWasRead = false;
778 AutofillValue value = null;
779
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 synchronized (mLock) {
781 // If the session is gone some fields might still be highlighted, hence we have to
782 // remove the isAutofilled property even if no sessions are active.
783 if (mLastAutofilledData == null) {
784 view.setAutofilled(false);
785 } else {
786 id = getAutofillId(view);
787 if (mLastAutofilledData.containsKey(id)) {
788 value = view.getAutofillValue();
789 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700790
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700791 if (Objects.equals(mLastAutofilledData.get(id), value)) {
792 view.setAutofilled(true);
793 } else {
794 view.setAutofilled(false);
795 mLastAutofilledData.remove(id);
796 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700797 } else {
798 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700799 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700800 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700801
Felipe Lemec24a56a2017-08-03 14:27:57 -0700802 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700803 if (sVerbose && mEnabled) {
804 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
805 + getStateAsStringLocked());
806 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700807 return;
808 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800809
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700810 if (id == null) {
811 id = getAutofillId(view);
812 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700813
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700814 if (!valueWasRead) {
815 value = view.getAutofillValue();
816 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700817
Felipe Leme0aa4c502017-04-26 12:36:01 -0700818 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700819 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800820 }
821
Felipe Lemebab851c2017-02-03 18:45:08 -0800822 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700823 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800824 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700825 * @param view the virtual view parent.
826 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800827 * @param value new value of the child.
828 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700829 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700830 if (!hasAutofillFeature()) {
831 return;
832 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700833 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700834 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700835 return;
836 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800837
Felipe Leme6dcec872017-05-25 11:24:23 -0700838 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700839 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700840 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800841 }
842
843 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700844 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800845 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700846 * <p>This method is typically called by {@link View Views} that manage virtual views; for
847 * example, when the view is rendering an {@code HTML} page with a form and virtual views
848 * that represent the HTML elements, it should call this method after the form is submitted and
849 * another page is rendered.
850 *
851 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
852 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800853 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700854 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700855 if (!hasAutofillFeature()) {
856 return;
857 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700858 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700859 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700860 return;
861 }
Svet Ganov782043c2017-02-11 00:52:02 +0000862
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700863 finishSessionLocked();
864 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800865 }
866
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700867 /**
868 * Called to indicate the current autofill context should be cancelled.
869 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700870 * <p>This method is typically called by {@link View Views} that manage virtual views; for
871 * example, when the view is rendering an {@code HTML} page with a form and virtual views
872 * that represent the HTML elements, it should call this method if the user does not post the
873 * form but moves to another form in this page.
874 *
875 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
876 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700877 */
878 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700879 if (!hasAutofillFeature()) {
880 return;
881 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700882 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700883 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700884 return;
885 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700886
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700887 cancelSessionLocked();
888 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700889 }
890
Svet Ganovf965b872017-04-28 16:34:02 -0700891 /** @hide */
892 public void disableOwnedAutofillServices() {
893 disableAutofillServices();
894 }
895
Svet Ganovf20a0372017-04-10 17:08:05 -0700896 /**
897 * If the app calling this API has enabled autofill services they
898 * will be disabled.
899 */
Svet Ganovf965b872017-04-28 16:34:02 -0700900 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700901 if (!hasAutofillFeature()) {
902 return;
903 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700904 try {
905 mService.disableOwnedAutofillServices(mContext.getUserId());
906 } catch (RemoteException e) {
907 throw e.rethrowFromSystemServer();
908 }
909 }
910
Felipe Lemedb041182017-04-21 17:33:38 -0700911 /**
912 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
913 * enabled for the current user, or {@code false} otherwise.
914 */
915 public boolean hasEnabledAutofillServices() {
916 if (mService == null) return false;
917
918 try {
919 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
920 } catch (RemoteException e) {
921 throw e.rethrowFromSystemServer();
922 }
923 }
924
925 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -0700926 * Returns {@code true} if autofill is supported by the current device and
927 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -0700928 *
929 * <p>Autofill is typically supported, but it could be unsupported in cases like:
930 * <ol>
931 * <li>Low-end devices.
932 * <li>Device policy rules that forbid its usage.
933 * </ol>
934 */
935 public boolean isAutofillSupported() {
936 if (mService == null) return false;
937
938 try {
939 return mService.isServiceSupported(mContext.getUserId());
940 } catch (RemoteException e) {
941 throw e.rethrowFromSystemServer();
942 }
943 }
944
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700945 private AutofillClient getClientLocked() {
Felipe Lemebb567ae2017-10-04 09:56:21 -0700946 return mContext.getAutofillClient();
Svet Ganov782043c2017-02-11 00:52:02 +0000947 }
948
949 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700950 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700951 if (!hasAutofillFeature()) {
952 return;
953 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700954 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800955 // handling the cases where it's not RESULT_OK: it works fine if the service does not
956 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
957 // service set the extra and returned RESULT_CANCELED...
958
Felipe Leme9f9ee252017-04-27 13:56:22 -0700959 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800960
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700961 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700962 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700963 return;
964 }
965 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
966 final Bundle responseData = new Bundle();
967 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -0700968 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
969 if (newClientState != null) {
970 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
971 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700972 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700973 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
974 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700975 } catch (RemoteException e) {
976 Log.e(TAG, "Error delivering authentication result", e);
977 }
Svet Ganov782043c2017-02-11 00:52:02 +0000978 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800979 }
980
Felipe Leme640f30a2017-03-06 15:44:06 -0800981 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -0700982 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800983 }
984
Felipe Leme6dcec872017-05-25 11:24:23 -0700985 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -0700986 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800987 }
988
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700989 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
990 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700991 if (sVerbose) {
992 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -0700993 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -0800994 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700995 if (mState != STATE_UNKNOWN && (flags & FLAG_MANUAL_REQUEST) == 0) {
996 if (sVerbose) {
997 Log.v(TAG, "not automatically starting session for " + id
998 + " on state " + getStateAsStringLocked());
999 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001000 return;
1001 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001002 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001003 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001004 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -08001005 mCallback != null, flags, mContext.getOpPackageName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001006 if (mSessionId != NO_SESSION) {
1007 mState = STATE_ACTIVE;
1008 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001009 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -08001010 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001011 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -08001012 }
Svet Ganov782043c2017-02-11 00:52:02 +00001013 } catch (RemoteException e) {
1014 throw e.rethrowFromSystemServer();
1015 }
1016 }
1017
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001018 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001019 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001020
1021 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001022
Svet Ganov782043c2017-02-11 00:52:02 +00001023 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001024 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001025 } catch (RemoteException e) {
1026 throw e.rethrowFromSystemServer();
1027 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001028
Felipe Lemec24a56a2017-08-03 14:27:57 -07001029 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001030 }
1031
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001032 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001033 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001034
1035 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001036
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001037 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001038 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001039 } catch (RemoteException e) {
1040 throw e.rethrowFromSystemServer();
1041 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001042
Svet Ganov48f10a22017-04-26 18:49:30 -07001043 resetSessionLocked();
1044 }
1045
1046 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001047 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001048 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001049 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001050 mFillableIds = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001051 }
1052
Felipe Leme0aa4c502017-04-26 12:36:01 -07001053 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1054 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001055 if (sVerbose && action != ACTION_VIEW_EXITED) {
1056 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1057 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001058 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001059 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1060
Felipe Leme3461d3c2017-01-19 08:54:55 -08001061 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001062 if (restartIfNecessary) {
1063 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1064 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1065 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
1066 if (newId != mSessionId) {
1067 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1068 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001069 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme7f33cd32017-05-11 10:10:49 -07001070 final AutofillClient client = getClientLocked();
1071 if (client != null) {
1072 client.autofillCallbackResetableStateAvailable();
1073 }
1074 }
1075 } else {
1076 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1077 mContext.getUserId());
1078 }
1079
Felipe Leme3461d3c2017-01-19 08:54:55 -08001080 } catch (RemoteException e) {
1081 throw e.rethrowFromSystemServer();
1082 }
1083 }
Svet Ganov782043c2017-02-11 00:52:02 +00001084
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001085 private void ensureServiceClientAddedIfNeededLocked() {
1086 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001087 return;
1088 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001089
Svet Ganov782043c2017-02-11 00:52:02 +00001090 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001091 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001092 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001093 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
1094 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1095 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1096 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +00001097 } catch (RemoteException e) {
1098 throw e.rethrowFromSystemServer();
1099 }
1100 }
1101 }
1102
Felipe Lemee6010f22017-03-03 11:19:51 -08001103 /**
1104 * Registers a {@link AutofillCallback} to receive autofill events.
1105 *
1106 * @param callback callback to receive events.
1107 */
1108 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001109 if (!hasAutofillFeature()) {
1110 return;
1111 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001112 synchronized (mLock) {
1113 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001114
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001115 final boolean hadCallback = mCallback != null;
1116 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001117
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001118 if (!hadCallback) {
1119 try {
1120 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1121 } catch (RemoteException e) {
1122 throw e.rethrowFromSystemServer();
1123 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001124 }
1125 }
1126 }
1127
1128 /**
1129 * Unregisters a {@link AutofillCallback} to receive autofill events.
1130 *
1131 * @param callback callback to stop receiving events.
1132 */
1133 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001134 if (!hasAutofillFeature()) {
1135 return;
1136 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001137 synchronized (mLock) {
1138 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001139
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001140 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001141
Felipe Lemee6010f22017-03-03 11:19:51 -08001142 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001143 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001144 } catch (RemoteException e) {
1145 throw e.rethrowFromSystemServer();
1146 }
1147 }
1148 }
1149
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001150 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1151 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1152 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001153 if (anchor == null) {
1154 return;
1155 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001156
1157 AutofillCallback callback = null;
1158 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001159 if (mSessionId == sessionId) {
1160 AutofillClient client = getClientLocked();
1161
1162 if (client != null) {
1163 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1164 anchorBounds, presenter) && mCallback != null) {
1165 callback = mCallback;
1166 }
1167 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001168 }
1169 }
1170
1171 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001172 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001173 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001174 AutofillCallback.EVENT_INPUT_SHOWN);
1175 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001176 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001177 }
1178 }
1179 }
1180
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001181 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1182 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001183 synchronized (mLock) {
1184 if (sessionId == mSessionId) {
1185 AutofillClient client = getClientLocked();
1186 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001187 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001188 }
1189 }
1190 }
1191 }
1192
Svet Ganov48f10a22017-04-26 18:49:30 -07001193 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001194 synchronized (mLock) {
1195 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -07001196 if (!mEnabled || resetSession) {
1197 // Reset the session state
1198 resetSessionLocked();
1199 }
1200 if (resetClient) {
1201 // Reset connection to system
1202 mServiceClient = null;
1203 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001204 }
1205 }
1206
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001207 /**
1208 * Sets a view as autofilled if the current value is the {code targetValue}.
1209 *
1210 * @param view The view that is to be autofilled
1211 * @param targetValue The value we want to fill into view
1212 */
1213 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1214 AutofillValue currentValue = view.getAutofillValue();
1215 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001216 synchronized (mLock) {
1217 if (mLastAutofilledData == null) {
1218 mLastAutofilledData = new ParcelableMap(1);
1219 }
1220 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001221 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001222 view.setAutofilled(true);
1223 }
1224 }
1225
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001226 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001227 synchronized (mLock) {
1228 if (sessionId != mSessionId) {
1229 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001230 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001231
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001232 final AutofillClient client = getClientLocked();
1233 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001234 return;
1235 }
1236
1237 final int itemCount = ids.size();
1238 int numApplied = 0;
1239 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001240 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001241
1242 for (int i = 0; i < itemCount; i++) {
1243 final AutofillId id = ids.get(i);
1244 final AutofillValue value = values.get(i);
1245 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001246 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001247 if (view == null) {
1248 Log.w(TAG, "autofill(): no View with id " + viewId);
1249 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001250 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001251 if (id.isVirtual()) {
1252 if (virtualValues == null) {
1253 // Most likely there will be just one view with virtual children.
1254 virtualValues = new ArrayMap<>(1);
1255 }
1256 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1257 if (valuesByParent == null) {
1258 // We don't know the size yet, but usually it will be just a few fields...
1259 valuesByParent = new SparseArray<>(5);
1260 virtualValues.put(view, valuesByParent);
1261 }
1262 valuesByParent.put(id.getVirtualChildId(), value);
1263 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001264 // Mark the view as to be autofilled with 'value'
1265 if (mLastAutofilledData == null) {
1266 mLastAutofilledData = new ParcelableMap(itemCount - i);
1267 }
1268 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001269
1270 view.autofill(value);
1271
1272 // Set as autofilled if the values match now, e.g. when the value was updated
1273 // synchronously.
1274 // If autofill happens async, the view is set to autofilled in
1275 // notifyValueChanged.
1276 setAutofilledIfValuesIs(view, value);
1277
1278 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001279 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001280 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001281
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001282 if (virtualValues != null) {
1283 for (int i = 0; i < virtualValues.size(); i++) {
1284 final View parent = virtualValues.keyAt(i);
1285 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1286 parent.autofill(childrenValues);
1287 numApplied += childrenValues.size();
1288 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001289 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001290
Felipe Lemeb22d6352017-09-08 20:03:53 -07001291 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1292 .setPackageName(mContext.getPackageName())
1293 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1294 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001295 mMetricsLogger.write(log);
1296 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001297 }
1298
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001299 /**
1300 * Set the tracked views.
1301 *
1302 * @param trackedIds The views to be tracked
1303 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme27e20222017-05-18 15:24:11 -07001304 * @param fillableIds Views that might anchor FillUI.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001305 */
Felipe Leme27e20222017-05-18 15:24:11 -07001306 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1307 boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001308 synchronized (mLock) {
1309 if (mEnabled && mSessionId == sessionId) {
1310 if (saveOnAllViewsInvisible) {
1311 mTrackedViews = new TrackedViews(trackedIds);
1312 } else {
1313 mTrackedViews = null;
1314 }
Felipe Leme27e20222017-05-18 15:24:11 -07001315 if (fillableIds != null) {
1316 if (mFillableIds == null) {
1317 mFillableIds = new ArraySet<>(fillableIds.length);
1318 }
1319 for (AutofillId id : fillableIds) {
1320 mFillableIds.add(id);
1321 }
1322 if (sVerbose) {
1323 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1324 + ", mFillableIds" + mFillableIds);
1325 }
1326 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001327 }
1328 }
1329 }
1330
Felipe Lemec24a56a2017-08-03 14:27:57 -07001331 private void setSaveUiState(int sessionId, boolean shown) {
1332 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1333 synchronized (mLock) {
1334 if (mSessionId != NO_SESSION) {
1335 // Race condition: app triggered a new session after the previous session was
1336 // finished but before server called setSaveUiState() - need to cancel the new
1337 // session to avoid further inconsistent behavior.
1338 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1339 + ") called on existing session " + mSessionId + "; cancelling it");
1340 cancelSessionLocked();
1341 }
1342 if (shown) {
1343 mSessionId = sessionId;
1344 mState = STATE_SHOWING_SAVE_UI;
1345 } else {
1346 mSessionId = NO_SESSION;
1347 mState = STATE_UNKNOWN;
1348 }
1349 }
1350 }
1351
Felipe Leme650f7ab2017-09-19 13:08:24 -07001352 /**
1353 * Marks the state of the session as finished.
1354 *
1355 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
1356 * FillResponse) or {@link #STATE_UNKNOWN} (because the session was removed).
1357 */
1358 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001359 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001360 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001361 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001362 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001363 }
1364 }
1365
Felipe Leme27e20222017-05-18 15:24:11 -07001366 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001367 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001368 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001369 if (anchor == null) {
1370 return;
1371 }
Felipe Leme27e20222017-05-18 15:24:11 -07001372 requestHideFillUi(id, anchor);
1373 }
1374
1375 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001376
1377 AutofillCallback callback = null;
1378 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001379 // We do not check the session id for two reasons:
1380 // 1. If local and remote session id are off sync the UI would be stuck shown
1381 // 2. There is a race between the user state being destroyed due the fill
1382 // service being uninstalled and the UI being dismissed.
1383 AutofillClient client = getClientLocked();
1384 if (client != null) {
1385 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1386 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001387 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001388 }
1389 }
1390
1391 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001392 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001393 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001394 AutofillCallback.EVENT_INPUT_HIDDEN);
1395 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001396 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001397 }
1398 }
1399 }
1400
Felipe Lemec7b45292017-09-19 09:06:20 -07001401 private void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
1402 if (sVerbose) {
1403 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
1404 + ", finished=" + sessionFinished);
1405 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001406 final View anchor = findView(id);
1407 if (anchor == null) {
1408 return;
1409 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001410
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001411 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001412 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001413 if (mSessionId == sessionId && getClientLocked() != null) {
1414 callback = mCallback;
1415 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001416 }
1417
1418 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001419 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001420 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001421 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1422 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001423 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001424 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001425 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001426
Felipe Lemec7b45292017-09-19 09:06:20 -07001427 if (sessionFinished) {
1428 // Callback call was "hijacked" to also update the session state.
Felipe Leme650f7ab2017-09-19 13:08:24 -07001429 setSessionFinished(STATE_FINISHED);
Felipe Leme4753bb02017-03-22 20:24:00 -07001430 }
1431 }
1432
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001433 /**
1434 * Get an array of viewIds from a List of {@link AutofillId}.
1435 *
1436 * @param autofillIds The autofill ids to convert
1437 *
1438 * @return The array of viewIds.
1439 */
Felipe Leme27e20222017-05-18 15:24:11 -07001440 // TODO: move to Helper as static method
1441 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1442 final int numIds = autofillIds.length;
1443 final int[] viewIds = new int[numIds];
1444 for (int i = 0; i < numIds; i++) {
1445 viewIds[i] = autofillIds[i].getViewId();
1446 }
1447
1448 return viewIds;
1449 }
1450
1451 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001452 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1453 final int numIds = autofillIds.size();
1454 final int[] viewIds = new int[numIds];
1455 for (int i = 0; i < numIds; i++) {
1456 viewIds[i] = autofillIds.get(i).getViewId();
1457 }
1458
1459 return viewIds;
1460 }
1461
1462 /**
1463 * Find a single view by its id.
1464 *
1465 * @param autofillId The autofill id of the view
1466 *
1467 * @return The view or {@code null} if view was not found
1468 */
1469 private View findView(@NonNull AutofillId autofillId) {
1470 final AutofillClient client = getClientLocked();
1471
1472 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001473 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001474 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001475
Phil Weaver846cda932017-06-15 10:10:06 -07001476 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001477 }
1478
Felipe Lemebb810922017-04-25 15:54:06 -07001479 /** @hide */
1480 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001481 return mService != null;
1482 }
1483
Felipe Lemec24a56a2017-08-03 14:27:57 -07001484 /** @hide */
1485 public void onPendingSaveUi(int operation, IBinder token) {
1486 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1487
1488 synchronized (mLock) {
1489 try {
1490 mService.onPendingSaveUi(operation, token);
1491 } catch (RemoteException e) {
1492 e.rethrowFromSystemServer();
1493 }
1494 }
1495 }
1496
1497 /** @hide */
1498 public void dump(String outerPrefix, PrintWriter pw) {
1499 pw.print(outerPrefix); pw.println("AutofillManager:");
1500 final String pfx = outerPrefix + " ";
1501 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001502 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001503 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1504 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1505 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1506 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1507 pw.print(pfx); pw.print("tracked views: ");
1508 if (mTrackedViews == null) {
1509 pw.println("null");
1510 } else {
1511 final String pfx2 = pfx + " ";
1512 pw.println();
1513 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1514 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1515 }
1516 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
1517 }
1518
Felipe Lemec7b45292017-09-19 09:06:20 -07001519 private String getStateAsStringLocked() {
1520 switch (mState) {
1521 case STATE_UNKNOWN:
1522 return "STATE_UNKNOWN";
1523 case STATE_ACTIVE:
1524 return "STATE_ACTIVE";
1525 case STATE_FINISHED:
1526 return "STATE_FINISHED";
1527 case STATE_SHOWING_SAVE_UI:
1528 return "STATE_SHOWING_SAVE_UI";
1529 default:
1530 return "INVALID:" + mState;
1531 }
1532 }
1533
Felipe Lemec24a56a2017-08-03 14:27:57 -07001534 private boolean isActiveLocked() {
1535 return mState == STATE_ACTIVE;
1536 }
1537
Felipe Lemec7b45292017-09-19 09:06:20 -07001538 private boolean isFinishedLocked() {
1539 return mState == STATE_FINISHED;
1540 }
1541
Felipe Leme9876a6f2017-05-30 15:47:28 -07001542 private void post(Runnable runnable) {
1543 final AutofillClient client = getClientLocked();
1544 if (client == null) {
1545 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1546 return;
1547 }
1548 client.runOnUiThread(runnable);
1549 }
1550
Felipe Lemee6010f22017-03-03 11:19:51 -08001551 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001552 * View tracking information. Once all tracked views become invisible the session is finished.
1553 */
1554 private class TrackedViews {
1555 /** Visible tracked views */
1556 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1557
1558 /** Invisible tracked views */
1559 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1560
1561 /**
1562 * Check if set is null or value is in set.
1563 *
1564 * @param set The set or null (== empty set)
1565 * @param value The value that might be in the set
1566 *
1567 * @return {@code true} iff set is not empty and value is in set
1568 */
Felipe Leme27e20222017-05-18 15:24:11 -07001569 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001570 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1571 return set != null && set.contains(value);
1572 }
1573
1574 /**
1575 * Add a value to a set. If set is null, create a new set.
1576 *
1577 * @param set The set or null (== empty set)
1578 * @param valueToAdd The value to add
1579 *
1580 * @return The set including the new value. If set was {@code null}, a set containing only
1581 * the new value.
1582 */
Felipe Leme27e20222017-05-18 15:24:11 -07001583 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001584 @NonNull
1585 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1586 if (set == null) {
1587 set = new ArraySet<>(1);
1588 }
1589
1590 set.add(valueToAdd);
1591
1592 return set;
1593 }
1594
1595 /**
1596 * Remove a value from a set.
1597 *
1598 * @param set The set or null (== empty set)
1599 * @param valueToRemove The value to remove
1600 *
1601 * @return The set without the removed value. {@code null} if set was null, or is empty
1602 * after removal.
1603 */
Felipe Leme27e20222017-05-18 15:24:11 -07001604 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001605 @Nullable
1606 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1607 if (set == null) {
1608 return null;
1609 }
1610
1611 set.remove(valueToRemove);
1612
1613 if (set.isEmpty()) {
1614 return null;
1615 }
1616
1617 return set;
1618 }
1619
1620 /**
1621 * Set the tracked views.
1622 *
1623 * @param trackedIds The views to be tracked
1624 */
Felipe Leme27e20222017-05-18 15:24:11 -07001625 TrackedViews(@Nullable AutofillId[] trackedIds) {
1626 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001627 if (trackedIds != null && client != null) {
1628 final boolean[] isVisible;
1629
1630 if (client.isVisibleForAutofill()) {
1631 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1632 } else {
1633 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001634 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001635 }
1636
Felipe Leme27e20222017-05-18 15:24:11 -07001637 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001638 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001639 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001640
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001641 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001642 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001643 } else {
1644 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1645 }
1646 }
1647 }
1648
Felipe Leme9f9ee252017-04-27 13:56:22 -07001649 if (sVerbose) {
1650 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001651 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1652 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1653 }
1654
1655 if (mVisibleTrackedIds == null) {
1656 finishSessionLocked();
1657 }
1658 }
1659
1660 /**
1661 * Called when a {@link View view's} visibility changes.
1662 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001663 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001664 * @param isVisible visible if the view is visible in the view hierarchy.
1665 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001666 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001667 AutofillClient client = getClientLocked();
1668
Felipe Leme9f9ee252017-04-27 13:56:22 -07001669 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001670 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001671 + isVisible);
1672 }
1673
1674 if (client != null && client.isVisibleForAutofill()) {
1675 if (isVisible) {
1676 if (isInSet(mInvisibleTrackedIds, id)) {
1677 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1678 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1679 }
1680 } else {
1681 if (isInSet(mVisibleTrackedIds, id)) {
1682 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1683 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1684 }
1685 }
1686 }
1687
1688 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001689 if (sVerbose) {
1690 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1691 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001692 finishSessionLocked();
1693 }
1694 }
1695
1696 /**
1697 * Called once the client becomes visible.
1698 *
1699 * @see AutofillClient#isVisibleForAutofill()
1700 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001701 void onVisibleForAutofillLocked() {
1702 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001703 // hence update the visibility state for all views.
1704 AutofillClient client = getClientLocked();
1705 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1706 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1707 if (client != null) {
1708 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001709 final ArrayList<AutofillId> orderedInvisibleIds =
1710 new ArrayList<>(mInvisibleTrackedIds);
1711 final boolean[] isVisible = client.getViewVisibility(
1712 getViewIds(orderedInvisibleIds));
1713
1714 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1715 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1716 final AutofillId id = orderedInvisibleIds.get(i);
1717 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001718 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1719
Felipe Leme9f9ee252017-04-27 13:56:22 -07001720 if (sDebug) {
1721 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001722 }
1723 } else {
1724 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1725 }
1726 }
1727 }
1728
1729 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001730 final ArrayList<AutofillId> orderedVisibleIds =
1731 new ArrayList<>(mVisibleTrackedIds);
1732 final boolean[] isVisible = client.getViewVisibility(
1733 getViewIds(orderedVisibleIds));
1734
1735 final int numVisibleTrackedIds = orderedVisibleIds.size();
1736 for (int i = 0; i < numVisibleTrackedIds; i++) {
1737 final AutofillId id = orderedVisibleIds.get(i);
1738
1739 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001740 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1741 } else {
1742 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1743
Felipe Leme9f9ee252017-04-27 13:56:22 -07001744 if (sDebug) {
1745 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001746 }
1747 }
1748 }
1749 }
1750
1751 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1752 mVisibleTrackedIds = updatedVisibleTrackedIds;
1753 }
1754
1755 if (mVisibleTrackedIds == null) {
1756 finishSessionLocked();
1757 }
1758 }
1759 }
1760
1761 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001762 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08001763 *
1764 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme744976e2017-07-31 11:34:14 -07001765 * enable / disable such views when the autofill UI affordance is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001766 */
1767 public abstract static class AutofillCallback {
1768
1769 /** @hide */
1770 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1771 @Retention(RetentionPolicy.SOURCE)
1772 public @interface AutofillEventType {}
1773
1774 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001775 * The autofill input UI affordance associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08001776 *
1777 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1778 * should be hidden upon receiving this event.
1779 */
1780 public static final int EVENT_INPUT_SHOWN = 1;
1781
1782 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001783 * The autofill input UI affordance associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001784 *
1785 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1786 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1787 */
1788 public static final int EVENT_INPUT_HIDDEN = 2;
1789
1790 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001791 * The autofill input UI affordance associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07001792 * autofill is not available.
1793 *
1794 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1795 * to avoid flickering, it could shown it upon receiving this event.
1796 */
1797 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1798
1799 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001800 * Called after a change in the autofill state associated with a view.
1801 *
1802 * @param view view associated with the change.
1803 *
1804 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1805 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001806 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1807 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001808
1809 /**
1810 * Called after a change in the autofill state associated with a virtual view.
1811 *
1812 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07001813 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08001814 *
1815 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1816 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001817 public void onAutofillEvent(@NonNull View view, int virtualId,
1818 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07001819 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001820 }
1821
Felipe Leme640f30a2017-03-06 15:44:06 -08001822 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1823 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001824
Felipe Leme640f30a2017-03-06 15:44:06 -08001825 AutofillManagerClient(AutofillManager autofillManager) {
1826 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001827 }
1828
1829 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001830 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001831 final AutofillManager afm = mAfm.get();
1832 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001833 afm.post(() -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001834 }
1835 }
1836
1837 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001838 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001839 final AutofillManager afm = mAfm.get();
1840 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001841 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001842 }
1843 }
1844
1845 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001846 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1847 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001848 final AutofillManager afm = mAfm.get();
1849 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001850 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001851 }
1852 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001853
1854 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001855 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1856 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001857 final AutofillManager afm = mAfm.get();
1858 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001859 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1860 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001861 }
1862 }
1863
1864 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001865 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001866 final AutofillManager afm = mAfm.get();
1867 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001868 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001869 }
1870 }
1871
1872 @Override
Felipe Lemec7b45292017-09-19 09:06:20 -07001873 public void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001874 final AutofillManager afm = mAfm.get();
1875 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001876 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinished));
Felipe Lemee6010f22017-03-03 11:19:51 -08001877 }
1878 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001879
1880 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07001881 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001882 final AutofillManager afm = mAfm.get();
1883 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001884 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001885 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001886 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07001887 } catch (IntentSender.SendIntentException e) {
1888 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1889 }
1890 });
1891 }
1892 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001893
1894 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07001895 public void setTrackedViews(int sessionId, AutofillId[] ids,
1896 boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001897 final AutofillManager afm = mAfm.get();
1898 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001899 afm.post(() ->
Felipe Leme27e20222017-05-18 15:24:11 -07001900 afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001901 );
1902 }
1903 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001904
1905 @Override
1906 public void setSaveUiState(int sessionId, boolean shown) {
1907 final AutofillManager afm = mAfm.get();
1908 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001909 afm.post(() -> afm.setSaveUiState(sessionId, shown));
1910 }
1911 }
1912
1913 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07001914 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001915 final AutofillManager afm = mAfm.get();
1916 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001917 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07001918 }
1919 }
Svet Ganov782043c2017-02-11 00:52:02 +00001920 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001921}