blob: deb627fb03e0302ef7b3abafd915bc42de34c0c8 [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 Leme17292d12017-10-24 14:03:10 -070027import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080028import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000029import android.content.Intent;
30import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080031import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070032import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000033import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070034import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000035import android.os.Parcelable;
Felipe Lemebc055b02018-01-05 17:04:10 -080036import android.os.RemoteCallback;
Felipe Leme3461d3c2017-01-19 08:54:55 -080037import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070038import android.service.autofill.AutofillService;
39import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080040import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070041import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070042import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080043import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070044import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080045import android.view.View;
46
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070047import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070048import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070049import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme637e05e2017-12-06 12:09:37 -080050import com.android.internal.util.Preconditions;
Felipe Leme4753bb02017-03-22 20:24:00 -070051
Felipe Lemec24a56a2017-08-03 14:27:57 -070052import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080053import java.lang.annotation.Retention;
54import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000055import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070056import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080057import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080058import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000059import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070060import java.util.Objects;
Felipe Lemebc055b02018-01-05 17:04:10 -080061import java.util.concurrent.CountDownLatch;
62import java.util.concurrent.TimeUnit;
Svet Ganov782043c2017-02-11 00:52:02 +000063
Koji Fukuiccec6a62017-10-18 17:48:40 +090064// TODO: use java.lang.ref.Cleaner once Android supports Java 9
65import sun.misc.Cleaner;
66
Felipe Leme3461d3c2017-01-19 08:54:55 -080067/**
Felipe Leme744976e2017-07-31 11:34:14 -070068 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
69 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070070 *
Felipe Leme744976e2017-07-31 11:34:14 -070071 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
72 * activity context; the autofill context is created when one of the following methods is called for
73 * the first time in an activity context, and the current user has an enabled autofill service:
74 *
75 * <ul>
76 * <li>{@link #notifyViewEntered(View)}
77 * <li>{@link #notifyViewEntered(View, int, Rect)}
78 * <li>{@link #requestAutofill(View)}
79 * </ul>
80 *
81 * <p>Tipically, the context is automatically created when the first view of the activity is
82 * focused because {@code View.onFocusChanged()} indirectly calls
83 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
84 * explicitly create it (for example, a custom view developer could offer a contextual menu action
85 * in a text-field view to let users manually request autofill).
86 *
87 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
88 * that represents the view hierarchy by calling
89 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
90 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
91 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
92 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
93 * the hierarchy.
94 *
95 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
96 * parses it looking for views that can be autofilled. If the service finds such views, it returns
97 * a data structure to the Android System containing the following optional info:
98 *
99 * <ul>
100 * <li>Datasets used to autofill subsets of views in the activity.
101 * <li>Id of views that the service can save their values for future autofilling.
102 * </ul>
103 *
104 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700105 * UI associated with the view, when the view is focused on and is part of a dataset.
106 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700107 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700108 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700109 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
110 *
111 * <p>When the service returns ids of savable views, the Android System keeps track of changes
112 * made to these views, so they can be used to determine if the autofill save UI is shown later.
113 *
114 * <p>The context is then finished when one of the following occurs:
115 *
116 * <ul>
117 * <li>{@link #commit()} is called or all savable views are gone.
118 * <li>{@link #cancel()} is called.
119 * </ul>
120 *
121 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700122 * 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 -0700123 * option to Save, the current value of the views is then sent to the autofill service.
124 *
125 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800126 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600127@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800128public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800129
Felipe Leme640f30a2017-03-06 15:44:06 -0800130 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800131
Svet Ganov782043c2017-02-11 00:52:02 +0000132 /**
133 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700134 *
Svet Ganov782043c2017-02-11 00:52:02 +0000135 * <p>
136 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000137 */
138 public static final String EXTRA_ASSIST_STRUCTURE =
139 "android.view.autofill.extra.ASSIST_STRUCTURE";
140
141 /**
142 * Intent extra: The result of an authentication operation. It is
143 * either a fully populated {@link android.service.autofill.FillResponse}
144 * or a fully populated {@link android.service.autofill.Dataset} if
145 * a response or a dataset is being authenticated respectively.
146 *
147 * <p>
148 * Type: {@link android.service.autofill.FillResponse} or a
149 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000150 */
151 public static final String EXTRA_AUTHENTICATION_RESULT =
152 "android.view.autofill.extra.AUTHENTICATION_RESULT";
153
Felipe Leme7320ca92017-03-29 15:09:54 -0700154 /**
155 * Intent extra: The optional extras provided by the
156 * {@link android.service.autofill.AutofillService}.
157 *
158 * <p>For example, when the service responds to a {@link
159 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
160 * a {@code FillResponse} that requires authentication, the Intent that launches the
161 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700162 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700163 *
Felipe Lemea9372382017-10-09 14:42:36 -0700164 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
165 * can also add this bundle to the {@link Intent} set as the
166 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
167 * so the bundle can be recovered later on
168 * {@link android.service.autofill.SaveRequest#getClientState()}.
169 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700170 * <p>
171 * Type: {@link android.os.Bundle}
172 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700173 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600174 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700175
Felipe Lemec24a56a2017-08-03 14:27:57 -0700176 /** @hide */
177 public static final String EXTRA_RESTORE_SESSION_TOKEN =
178 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
179
Felipe Lemebc055b02018-01-05 17:04:10 -0800180 /** @hide */
181 public static final String EXTRA_AVAILABLE_ALGORITHMS = "available_algorithms";
182 /** @hide */
183 public static final String EXTRA_DEFAULT_ALGORITHM = "default_algorithm";
184
Felipe Lemec24a56a2017-08-03 14:27:57 -0700185 private static final String SESSION_ID_TAG = "android:sessionId";
186 private static final String STATE_TAG = "android:state";
187 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
188
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700189
Felipe Leme0aa4c502017-04-26 12:36:01 -0700190 /** @hide */ public static final int ACTION_START_SESSION = 1;
191 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
192 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
193 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800194
Felipe Leme9f9ee252017-04-27 13:56:22 -0700195
196 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
197 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
198 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
199
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700200 /** Which bits in an authentication id are used for the dataset id */
201 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
202 /** How many bits in an authentication id are used for the dataset id */
203 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
204 /** @hide The index for an undefined data set */
205 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
206
207 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700208 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
209 *
210 * @hide
211 */
212 public static final int PENDING_UI_OPERATION_CANCEL = 1;
213
214 /**
215 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
216 *
217 * @hide
218 */
219 public static final int PENDING_UI_OPERATION_RESTORE = 2;
220
221 /**
222 * Initial state of the autofill context, set when there is no session (i.e., when
223 * {@link #mSessionId} is {@link #NO_SESSION}).
224 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700225 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
226 * the server.
227 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700228 * @hide
229 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700230 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700231
232 /**
233 * State where the autofill context hasn't been {@link #commit() finished} nor
234 * {@link #cancel() canceled} yet.
235 *
236 * @hide
237 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700238 public static final int STATE_ACTIVE = 1;
239
240 /**
241 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700242 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700243 *
244 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
245 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
246 *
247 * @hide
248 */
249 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700250
251 /**
252 * State where the autofill context has been {@link #commit() finished} but the server still has
253 * a session because the Save UI hasn't been dismissed yet.
254 *
255 * @hide
256 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700257 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700258
259 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700260 * State where the autofill is disabled because the service cannot autofill the activity at all.
261 *
262 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
263 * (and {@link #requestAutofill(View, int, Rect)}).
264 *
265 * @hide
266 */
267 public static final int STATE_DISABLED_BY_SERVICE = 4;
268
269 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800270 * Timeout in ms for calls to the field classification service.
271 * @hide
272 */
273 public static final int FC_SERVICE_TIMEOUT = 5000;
274
275 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700276 * Makes an authentication id from a request id and a dataset id.
277 *
278 * @param requestId The request id.
279 * @param datasetId The dataset id.
280 * @return The authentication id.
281 * @hide
282 */
283 public static int makeAuthenticationId(int requestId, int datasetId) {
284 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
285 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
286 }
287
288 /**
289 * Gets the request id from an authentication id.
290 *
291 * @param authRequestId The authentication id.
292 * @return The request id.
293 * @hide
294 */
295 public static int getRequestIdFromAuthenticationId(int authRequestId) {
296 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
297 }
298
299 /**
300 * Gets the dataset id from an authentication id.
301 *
302 * @param authRequestId The authentication id.
303 * @return The dataset id.
304 * @hide
305 */
306 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
307 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
308 }
309
Felipe Leme4753bb02017-03-22 20:24:00 -0700310 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000311
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700312 /**
313 * There is currently no session running.
314 * {@hide}
315 */
316 public static final int NO_SESSION = Integer.MIN_VALUE;
317
Svet Ganov782043c2017-02-11 00:52:02 +0000318 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700319
320 private final Object mLock = new Object();
321
322 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000323 private IAutoFillManagerClient mServiceClient;
324
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700325 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900326 private Cleaner mServiceClientCleaner;
327
328 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800329 private AutofillCallback mCallback;
330
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700331 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000332
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700333 @GuardedBy("mLock")
334 private int mSessionId = NO_SESSION;
335
336 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700337 private int mState = STATE_UNKNOWN;
338
339 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000340 private boolean mEnabled;
341
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700342 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700343 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700344 @Nullable private ParcelableMap mLastAutofilledData;
345
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700346 /** If view tracking is enabled, contains the tracking state */
347 @GuardedBy("mLock")
348 @Nullable private TrackedViews mTrackedViews;
349
Felipe Leme27e20222017-05-18 15:24:11 -0700350 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
351 @GuardedBy("mLock")
352 @Nullable private ArraySet<AutofillId> mFillableIds;
353
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700354 /** If set, session is commited when the field is clicked. */
355 @GuardedBy("mLock")
356 @Nullable private AutofillId mSaveTriggerId;
357
358 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
359 @GuardedBy("mLock")
360 private boolean mSaveOnFinish;
361
Svet Ganov782043c2017-02-11 00:52:02 +0000362 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800363 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000364 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000365 * Asks the client to start an authentication flow.
366 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700367 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000368 * @param intent The authentication intent.
369 * @param fillInIntent The authentication fill-in intent.
370 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700371 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
372 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800373
374 /**
375 * Tells the client this manager has state to be reset.
376 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700377 void autofillCallbackResetableStateAvailable();
378
379 /**
380 * Request showing the autofill UI.
381 *
382 * @param anchor The real view the UI needs to anchor to.
383 * @param width The width of the fill UI content.
384 * @param height The height of the fill UI content.
385 * @param virtualBounds The bounds of the virtual decendant of the anchor.
386 * @param presenter The presenter that controls the fill UI window.
387 * @return Whether the UI was shown.
388 */
389 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
390 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
391
392 /**
393 * Request hiding the autofill UI.
394 *
395 * @return Whether the UI was hidden.
396 */
397 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700398
399 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700400 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700401 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700402 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700403 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700404 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700405
406 /**
407 * Checks is the client is currently visible as understood by autofill.
408 *
409 * @return {@code true} if the client is currently visible
410 */
411 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700412
413 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700414 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700415 *
Phil Weaver846cda932017-06-15 10:10:06 -0700416 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700417 *
Felipe Leme27e20222017-05-18 15:24:11 -0700418 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700419 */
Phil Weaver846cda932017-06-15 10:10:06 -0700420 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700421
422 /**
423 * Finds a view by traversing the hierarchies of the client.
424 *
Phil Weaver846cda932017-06-15 10:10:06 -0700425 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700426 *
427 * @return The view, or {@code null} if not found
428 */
Phil Weaver846cda932017-06-15 10:10:06 -0700429 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700430
431 /**
432 * Runs the specified action on the UI thread.
433 */
434 void runOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700435
436 /**
437 * Gets the complete component name of this client.
438 */
439 ComponentName getComponentName();
Svet Ganov782043c2017-02-11 00:52:02 +0000440 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800441
442 /**
443 * @hide
444 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800445 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800446 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800447 mService = service;
448 }
449
450 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700451 * Restore state after activity lifecycle
452 *
453 * @param savedInstanceState The state to be restored
454 *
455 * {@hide}
456 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700457 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700458 if (!hasAutofillFeature()) {
459 return;
460 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700461 synchronized (mLock) {
462 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
463
Felipe Lemec24a56a2017-08-03 14:27:57 -0700464 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700465 Log.w(TAG, "New session was started before onCreate()");
466 return;
467 }
468
469 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700470 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700471
472 if (mSessionId != NO_SESSION) {
473 ensureServiceClientAddedIfNeededLocked();
474
Felipe Leme637e05e2017-12-06 12:09:37 -0800475 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700476 if (client != null) {
477 try {
478 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
479 mContext.getActivityToken(), mServiceClient.asBinder());
480
481 if (!sessionWasRestored) {
482 Log.w(TAG, "Session " + mSessionId + " could not be restored");
483 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700484 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700485 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700486 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700487 Log.d(TAG, "session " + mSessionId + " was restored");
488 }
489
490 client.autofillCallbackResetableStateAvailable();
491 }
492 } catch (RemoteException e) {
493 Log.e(TAG, "Could not figure out if there was an autofill session", e);
494 }
495 }
496 }
497 }
498 }
499
500 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700501 * Called once the client becomes visible.
502 *
503 * @see AutofillClient#isVisibleForAutofill()
504 *
505 * {@hide}
506 */
507 public void onVisibleForAutofill() {
508 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700509 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700510 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700511 }
512 }
513 }
514
515 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700516 * Save state before activity lifecycle
517 *
518 * @param outState Place to store the state
519 *
520 * {@hide}
521 */
522 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700523 if (!hasAutofillFeature()) {
524 return;
525 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700526 synchronized (mLock) {
527 if (mSessionId != NO_SESSION) {
528 outState.putInt(SESSION_ID_TAG, mSessionId);
529 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700530 if (mState != STATE_UNKNOWN) {
531 outState.putInt(STATE_TAG, mState);
532 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700533 if (mLastAutofilledData != null) {
534 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
535 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700536 }
537 }
538
539 /**
540 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700541 *
542 * <p>Typically used to determine whether the option to explicitly request autofill should
543 * be offered - see {@link #requestAutofill(View)}.
544 *
545 * @return whether autofill is enabled for the current user.
546 */
547 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800548 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700549 return false;
550 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700551 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800552 if (isDisabledByServiceLocked()) {
553 return false;
554 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700555 ensureServiceClientAddedIfNeededLocked();
556 return mEnabled;
557 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700558 }
559
560 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700561 * Should always be called from {@link AutofillService#getFillEventHistory()}.
562 *
563 * @hide
564 */
565 @Nullable public FillEventHistory getFillEventHistory() {
566 try {
567 return mService.getFillEventHistory();
568 } catch (RemoteException e) {
569 e.rethrowFromSystemServer();
570 return null;
571 }
572 }
573
574 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700575 * Explicitly requests a new autofill context.
576 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700577 * <p>Normally, the autofill context is automatically started if necessary when
578 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
579 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
580 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700581 *
582 * @param view view requesting the new autofill context.
583 */
584 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700585 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700586 }
587
588 /**
589 * Explicitly requests a new autofill context for virtual views.
590 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700591 * <p>Normally, the autofill context is automatically started if necessary when
592 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
593 * cases where it must be explicitly started. For example, when the virtual view offers an
594 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700595 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700596 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
597 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
598 * the absolute bounds could be calculated by:
599 *
600 * <pre class="prettyprint">
601 * int offset[] = new int[2];
602 * getLocationOnScreen(offset);
603 * Rect absBounds = new Rect(bounds.left + offset[0],
604 * bounds.top + offset[1],
605 * bounds.right + offset[0], bounds.bottom + offset[1]);
606 * </pre>
607 *
608 * @param view the virtual view parent.
609 * @param virtualId id identifying the virtual child inside the parent view.
610 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700611 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700612 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
613 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700614 }
615
Felipe Leme2ac463e2017-03-13 14:06:25 -0700616 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700617 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800618 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700619 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800620 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700621 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700622 notifyViewEntered(view, 0);
623 }
624
Felipe Leme17292d12017-10-24 14:03:10 -0700625 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800626 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700627 if (sVerbose) {
628 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
629 + ") on state " + getStateAsStringLocked());
630 }
631 return true;
632 }
Felipe Leme3103c632017-12-18 15:05:14 -0800633 if (sVerbose && isFinishedLocked()) {
634 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
635 + ") on state " + getStateAsStringLocked());
Felipe Leme17292d12017-10-24 14:03:10 -0700636 }
637 return false;
638 }
639
Felipe Lemed1146422017-04-26 10:17:05 -0700640 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700641 if (!hasAutofillFeature()) {
642 return;
643 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700644 AutofillCallback callback = null;
645 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700646 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
Felipe Lemec7b45292017-09-19 09:06:20 -0700647
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700648 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000649
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700650 if (!mEnabled) {
651 if (mCallback != null) {
652 callback = mCallback;
653 }
654 } else {
655 final AutofillId id = getAutofillId(view);
656 final AutofillValue value = view.getAutofillValue();
657
Felipe Lemec24a56a2017-08-03 14:27:57 -0700658 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700659 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700660 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700661 } else {
662 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700663 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700664 }
Felipe Leme24aae152017-03-15 12:33:01 -0700665 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800666 }
667
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700668 if (callback != null) {
669 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000670 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800671 }
672
673 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700674 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800675 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700676 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800677 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700678 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700679 if (!hasAutofillFeature()) {
680 return;
681 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700682 synchronized (mLock) {
683 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800684
Felipe Lemec24a56a2017-08-03 14:27:57 -0700685 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700686 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800687
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700688 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700689 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700690 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800691 }
692 }
693
694 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700695 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700696 *
697 * @param view {@link View} that was exited.
698 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700699 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700700 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
701 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
702 }
703
704 /**
705 * Called when a virtual view's visibility changed.
706 *
707 * @param view {@link View} that was exited.
708 * @param virtualId id identifying the virtual child inside the parent view.
709 * @param isVisible visible if the view is visible in the view hierarchy.
710 */
711 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
712 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
713 }
714
715 /**
716 * Called when a view/virtual view's visibility changed.
717 *
718 * @param view {@link View} that was exited.
719 * @param virtualId id identifying the virtual child inside the parent view.
720 * @param isVisible visible if the view is visible in the view hierarchy.
721 * @param virtual Whether the view is virtual.
722 */
723 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
724 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700725 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700726 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700727 final AutofillId id = virtual ? getAutofillId(view, virtualId)
728 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700729 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700730 if (mFillableIds.contains(id)) {
731 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
732 requestHideFillUi(id, view);
733 }
734 }
735 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700736 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700737 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700738 }
739 }
740 }
741
742 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700743 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800744 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700745 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
746 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
747 * the absolute bounds could be calculated by:
748 *
749 * <pre class="prettyprint">
750 * int offset[] = new int[2];
751 * getLocationOnScreen(offset);
752 * Rect absBounds = new Rect(bounds.left + offset[0],
753 * bounds.top + offset[1],
754 * bounds.right + offset[0], bounds.bottom + offset[1]);
755 * </pre>
756 *
757 * @param view the virtual view parent.
758 * @param virtualId id identifying the virtual child inside the parent view.
759 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800760 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700761 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
762 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700763 }
764
Felipe Leme6dcec872017-05-25 11:24:23 -0700765 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700766 if (!hasAutofillFeature()) {
767 return;
768 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700769 AutofillCallback callback = null;
770 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700771 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
772
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700773 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000774
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700775 if (!mEnabled) {
776 if (mCallback != null) {
777 callback = mCallback;
778 }
779 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700780 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700781
Felipe Lemec24a56a2017-08-03 14:27:57 -0700782 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700783 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700784 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700785 } else {
786 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700787 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700788 }
Felipe Leme24aae152017-03-15 12:33:01 -0700789 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800790 }
791
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700792 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700793 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700794 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800795 }
796 }
797
798 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700799 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800800 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700801 * @param view the virtual view parent.
802 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800803 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700804 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700805 if (!hasAutofillFeature()) {
806 return;
807 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700808 synchronized (mLock) {
809 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800810
Felipe Lemec24a56a2017-08-03 14:27:57 -0700811 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700812 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800813
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700814 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700815 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700816 }
Svet Ganov782043c2017-02-11 00:52:02 +0000817 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800818 }
819
820 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800821 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800822 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800823 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800824 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700825 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700826 if (!hasAutofillFeature()) {
827 return;
828 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700829 AutofillId id = null;
830 boolean valueWasRead = false;
831 AutofillValue value = null;
832
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700833 synchronized (mLock) {
834 // If the session is gone some fields might still be highlighted, hence we have to
835 // remove the isAutofilled property even if no sessions are active.
836 if (mLastAutofilledData == null) {
837 view.setAutofilled(false);
838 } else {
839 id = getAutofillId(view);
840 if (mLastAutofilledData.containsKey(id)) {
841 value = view.getAutofillValue();
842 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700843
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700844 if (Objects.equals(mLastAutofilledData.get(id), value)) {
845 view.setAutofilled(true);
846 } else {
847 view.setAutofilled(false);
848 mLastAutofilledData.remove(id);
849 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700850 } else {
851 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700852 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700853 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700854
Felipe Lemec24a56a2017-08-03 14:27:57 -0700855 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700856 if (sVerbose && mEnabled) {
857 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
858 + getStateAsStringLocked());
859 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700860 return;
861 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800862
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700863 if (id == null) {
864 id = getAutofillId(view);
865 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700866
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700867 if (!valueWasRead) {
868 value = view.getAutofillValue();
869 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700870
Felipe Leme0aa4c502017-04-26 12:36:01 -0700871 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700872 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800873 }
874
Felipe Lemebab851c2017-02-03 18:45:08 -0800875 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700876 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800877 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700878 * @param view the virtual view parent.
879 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800880 * @param value new value of the child.
881 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700882 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700883 if (!hasAutofillFeature()) {
884 return;
885 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700886 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700887 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700888 return;
889 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800890
Felipe Leme6dcec872017-05-25 11:24:23 -0700891 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700892 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700893 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800894 }
895
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700896
897 /**
898 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
899 *
900 * @hide
901 */
902 public void notifyViewClicked(View view) {
903 final AutofillId id = view.getAutofillId();
904
905 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
906
907 synchronized (mLock) {
908 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
909 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
910 commitLocked();
911 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
912 mContext.getPackageName());
913 }
914 }
915 }
916
917 /**
918 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
919 *
920 * @hide
921 */
922 public void onActivityFinished() {
923 if (!hasAutofillFeature()) {
924 return;
925 }
926 synchronized (mLock) {
927 if (mSaveOnFinish) {
Felipe Leme601d2202017-11-17 08:21:23 -0800928 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700929 commitLocked();
930 } else {
931 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
932 cancelLocked();
933 }
934 }
935 }
936
Felipe Lemebab851c2017-02-03 18:45:08 -0800937 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700938 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800939 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700940 * <p>This method is typically called by {@link View Views} that manage virtual views; for
941 * example, when the view is rendering an {@code HTML} page with a form and virtual views
942 * that represent the HTML elements, it should call this method after the form is submitted and
943 * another page is rendered.
944 *
945 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
946 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800947 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700948 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700949 if (!hasAutofillFeature()) {
950 return;
951 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700952 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700953 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700954 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800955 }
956
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700957 private void commitLocked() {
958 if (!mEnabled && !isActiveLocked()) {
959 return;
960 }
961 finishSessionLocked();
962 }
963
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700964 /**
965 * Called to indicate the current autofill context should be cancelled.
966 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700967 * <p>This method is typically called by {@link View Views} that manage virtual views; for
968 * example, when the view is rendering an {@code HTML} page with a form and virtual views
969 * that represent the HTML elements, it should call this method if the user does not post the
970 * form but moves to another form in this page.
971 *
972 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
973 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700974 */
975 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -0800976 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -0700977 if (!hasAutofillFeature()) {
978 return;
979 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700980 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700981 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700982 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700983 }
984
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700985 private void cancelLocked() {
986 if (!mEnabled && !isActiveLocked()) {
987 return;
988 }
989 cancelSessionLocked();
990 }
991
Svet Ganovf965b872017-04-28 16:34:02 -0700992 /** @hide */
993 public void disableOwnedAutofillServices() {
994 disableAutofillServices();
995 }
996
Svet Ganovf20a0372017-04-10 17:08:05 -0700997 /**
998 * If the app calling this API has enabled autofill services they
999 * will be disabled.
1000 */
Svet Ganovf965b872017-04-28 16:34:02 -07001001 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001002 if (!hasAutofillFeature()) {
1003 return;
1004 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001005 try {
1006 mService.disableOwnedAutofillServices(mContext.getUserId());
1007 } catch (RemoteException e) {
1008 throw e.rethrowFromSystemServer();
1009 }
1010 }
1011
Felipe Lemedb041182017-04-21 17:33:38 -07001012 /**
1013 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1014 * enabled for the current user, or {@code false} otherwise.
1015 */
1016 public boolean hasEnabledAutofillServices() {
1017 if (mService == null) return false;
1018
1019 try {
1020 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1021 } catch (RemoteException e) {
1022 throw e.rethrowFromSystemServer();
1023 }
1024 }
1025
1026 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001027 * Returns the component name of the {@link AutofillService} that is enabled for the current
1028 * user.
1029 */
1030 @Nullable
1031 public ComponentName getAutofillServiceComponentName() {
1032 if (mService == null) return null;
1033
1034 try {
1035 return mService.getAutofillServiceComponentName();
1036 } catch (RemoteException e) {
1037 throw e.rethrowFromSystemServer();
1038 }
1039 }
1040
1041 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001042 * Gets the user data used for
1043 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001044 *
Felipe Leme27f45732017-12-22 09:05:22 -08001045 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1046 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1047 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001048 *
Felipe Leme452886a2017-11-27 13:09:13 -08001049 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1050 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001051 */
Felipe Leme452886a2017-11-27 13:09:13 -08001052 @Nullable public UserData getUserData() {
1053 try {
1054 return mService.getUserData();
1055 } catch (RemoteException e) {
1056 e.rethrowFromSystemServer();
1057 return null;
1058 }
1059 }
1060
1061 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001062 * Sets the user data used for
1063 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001064 *
1065 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1066 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1067 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001068 */
Felipe Leme452886a2017-11-27 13:09:13 -08001069 public void setUserData(@Nullable UserData userData) {
1070 try {
1071 mService.setUserData(userData);
1072 } catch (RemoteException e) {
1073 e.rethrowFromSystemServer();
1074 }
1075 }
1076
1077 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001078 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1079 * enabled.
1080 *
1081 * <p>As field classification is an expensive operation, it could be disabled, either
1082 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1083 * permanently (for example, because the device is a low-level device).
1084 *
1085 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1086 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1087 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001088 */
Felipe Leme329d0402017-12-06 09:22:43 -08001089 public boolean isFieldClassificationEnabled() {
1090 try {
1091 return mService.isFieldClassificationEnabled();
1092 } catch (RemoteException e) {
1093 e.rethrowFromSystemServer();
1094 return false;
1095 }
1096 }
1097
1098 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001099 * Gets the name of the default algorithm used for
1100 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1101 *
1102 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1103 * set.
1104 *
1105 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1106 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1107 * the user.
1108 */
Felipe Lemebc055b02018-01-05 17:04:10 -08001109 // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
1110 // the ExtService manifest (instead of calling the service)
Felipe Leme27f45732017-12-22 09:05:22 -08001111 @Nullable
1112 public String getDefaultFieldClassificationAlgorithm() {
Felipe Lemebc055b02018-01-05 17:04:10 -08001113 final SyncRemoteCallbackListener<String> listener =
1114 new SyncRemoteCallbackListener<String>() {
1115
1116 @Override
1117 String getResult(Bundle result) {
1118 return result == null ? null : result.getString(EXTRA_DEFAULT_ALGORITHM);
1119 }
1120 };
1121
Felipe Leme27f45732017-12-22 09:05:22 -08001122 try {
Felipe Lemebc055b02018-01-05 17:04:10 -08001123 mService.getDefaultFieldClassificationAlgorithm(new RemoteCallback(listener));
1124 return listener.getResult(FC_SERVICE_TIMEOUT);
Felipe Leme27f45732017-12-22 09:05:22 -08001125 } catch (RemoteException e) {
1126 e.rethrowFromSystemServer();
1127 return null;
1128 }
1129 }
1130
1131 /**
1132 * Gets the name of all algorithms currently available for
1133 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1134 *
1135 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001136 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1137 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001138 */
Felipe Lemebc055b02018-01-05 17:04:10 -08001139 // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
1140 // the ExtService manifest (instead of calling the service)
Felipe Leme27f45732017-12-22 09:05:22 -08001141 @NonNull
1142 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemebc055b02018-01-05 17:04:10 -08001143 final SyncRemoteCallbackListener<List<String>> listener =
1144 new SyncRemoteCallbackListener<List<String>>() {
1145
1146 @Override
1147 List<String> getResult(Bundle result) {
1148 List<String> algorithms = null;
1149 if (result != null) {
1150 final String[] asArray = result.getStringArray(EXTRA_AVAILABLE_ALGORITHMS);
1151 if (asArray != null) {
1152 algorithms = Arrays.asList(asArray);
1153 }
1154 }
1155 return algorithms != null ? algorithms : Collections.emptyList();
1156 }
1157 };
1158
Felipe Leme27f45732017-12-22 09:05:22 -08001159 try {
Felipe Lemebc055b02018-01-05 17:04:10 -08001160 mService.getAvailableFieldClassificationAlgorithms(new RemoteCallback(listener));
1161 return listener.getResult(FC_SERVICE_TIMEOUT);
Felipe Leme27f45732017-12-22 09:05:22 -08001162 } catch (RemoteException e) {
1163 e.rethrowFromSystemServer();
1164 return null;
1165 }
1166 }
1167
1168 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001169 * Returns {@code true} if autofill is supported by the current device and
1170 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001171 *
1172 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1173 * <ol>
1174 * <li>Low-end devices.
1175 * <li>Device policy rules that forbid its usage.
1176 * </ol>
1177 */
1178 public boolean isAutofillSupported() {
1179 if (mService == null) return false;
1180
1181 try {
1182 return mService.isServiceSupported(mContext.getUserId());
1183 } catch (RemoteException e) {
1184 throw e.rethrowFromSystemServer();
1185 }
1186 }
1187
Felipe Leme637e05e2017-12-06 12:09:37 -08001188 // Note: don't need to use locked suffix because mContext is final.
1189 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001190 final AutofillClient client = mContext.getAutofillClient();
1191 if (client == null && sDebug) {
1192 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1193 + mContext);
1194 }
1195 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001196 }
1197
1198 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001199 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -07001200 if (!hasAutofillFeature()) {
1201 return;
1202 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001203 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001204 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1205 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1206 // service set the extra and returned RESULT_CANCELED...
1207
Felipe Leme9f9ee252017-04-27 13:56:22 -07001208 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001209
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001210 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001211 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001212 return;
1213 }
1214 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1215 final Bundle responseData = new Bundle();
1216 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001217 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1218 if (newClientState != null) {
1219 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1220 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001221 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001222 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1223 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001224 } catch (RemoteException e) {
1225 Log.e(TAG, "Error delivering authentication result", e);
1226 }
Svet Ganov782043c2017-02-11 00:52:02 +00001227 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001228 }
1229
Felipe Leme640f30a2017-03-06 15:44:06 -08001230 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001231 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001232 }
1233
Felipe Leme6dcec872017-05-25 11:24:23 -07001234 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001235 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001236 }
1237
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001238 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1239 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001240 if (sVerbose) {
1241 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -07001242 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001243 }
Felipe Leme3103c632017-12-18 15:05:14 -08001244 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001245 if (sVerbose) {
1246 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001247 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001248 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001249 return;
1250 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001251 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001252 final AutofillClient client = getClient();
1253 if (client == null) return; // NOTE: getClient() already logd it..
1254
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001255 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001256 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001257 mCallback != null, flags, client.getComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001258 if (mSessionId != NO_SESSION) {
1259 mState = STATE_ACTIVE;
1260 }
Felipe Leme637e05e2017-12-06 12:09:37 -08001261 client.autofillCallbackResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001262 } catch (RemoteException e) {
1263 throw e.rethrowFromSystemServer();
1264 }
1265 }
1266
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001267 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001268 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001269
1270 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001271
Svet Ganov782043c2017-02-11 00:52:02 +00001272 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001273 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001274 } catch (RemoteException e) {
1275 throw e.rethrowFromSystemServer();
1276 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001277
Felipe Lemec24a56a2017-08-03 14:27:57 -07001278 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001279 }
1280
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001281 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001282 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001283
1284 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001285
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001286 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001287 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001288 } catch (RemoteException e) {
1289 throw e.rethrowFromSystemServer();
1290 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001291
Svet Ganov48f10a22017-04-26 18:49:30 -07001292 resetSessionLocked();
1293 }
1294
1295 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001296 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001297 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001298 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001299 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001300 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001301 }
1302
Felipe Leme0aa4c502017-04-26 12:36:01 -07001303 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1304 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001305 if (sVerbose && action != ACTION_VIEW_EXITED) {
1306 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1307 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001308 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001309 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1310
Felipe Leme3461d3c2017-01-19 08:54:55 -08001311 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001312 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001313 final AutofillClient client = getClient();
1314 if (client == null) return; // NOTE: getClient() already logd it..
1315
Felipe Leme7f33cd32017-05-11 10:10:49 -07001316 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1317 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001318 mCallback != null, flags, client.getComponentName(), mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001319 if (newId != mSessionId) {
1320 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1321 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001322 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme637e05e2017-12-06 12:09:37 -08001323 client.autofillCallbackResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001324 }
1325 } else {
1326 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1327 mContext.getUserId());
1328 }
1329
Felipe Leme3461d3c2017-01-19 08:54:55 -08001330 } catch (RemoteException e) {
1331 throw e.rethrowFromSystemServer();
1332 }
1333 }
Svet Ganov782043c2017-02-11 00:52:02 +00001334
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001335 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001336 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001337 return;
1338 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001339
Svet Ganov782043c2017-02-11 00:52:02 +00001340 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001341 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001342 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001343 final int userId = mContext.getUserId();
1344 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001345 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1346 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1347 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001348 final IAutoFillManager service = mService;
1349 final IAutoFillManagerClient serviceClient = mServiceClient;
1350 mServiceClientCleaner = Cleaner.create(this, () -> {
1351 try {
1352 service.removeClient(serviceClient, userId);
1353 } catch (RemoteException e) {
1354 }
1355 });
Svet Ganov782043c2017-02-11 00:52:02 +00001356 } catch (RemoteException e) {
1357 throw e.rethrowFromSystemServer();
1358 }
1359 }
1360 }
1361
Felipe Lemee6010f22017-03-03 11:19:51 -08001362 /**
1363 * Registers a {@link AutofillCallback} to receive autofill events.
1364 *
1365 * @param callback callback to receive events.
1366 */
1367 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001368 if (!hasAutofillFeature()) {
1369 return;
1370 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001371 synchronized (mLock) {
1372 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001373
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001374 final boolean hadCallback = mCallback != null;
1375 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001376
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001377 if (!hadCallback) {
1378 try {
1379 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1380 } catch (RemoteException e) {
1381 throw e.rethrowFromSystemServer();
1382 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001383 }
1384 }
1385 }
1386
1387 /**
1388 * Unregisters a {@link AutofillCallback} to receive autofill events.
1389 *
1390 * @param callback callback to stop receiving events.
1391 */
1392 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001393 if (!hasAutofillFeature()) {
1394 return;
1395 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001396 synchronized (mLock) {
1397 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001398
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001399 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001400
Felipe Lemee6010f22017-03-03 11:19:51 -08001401 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001402 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001403 } catch (RemoteException e) {
1404 throw e.rethrowFromSystemServer();
1405 }
1406 }
1407 }
1408
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001409 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1410 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1411 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001412 if (anchor == null) {
1413 return;
1414 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001415
1416 AutofillCallback callback = null;
1417 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001418 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001419 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001420
1421 if (client != null) {
1422 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1423 anchorBounds, presenter) && mCallback != null) {
1424 callback = mCallback;
1425 }
1426 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001427 }
1428 }
1429
1430 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001431 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001432 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001433 AutofillCallback.EVENT_INPUT_SHOWN);
1434 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001435 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001436 }
1437 }
1438 }
1439
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001440 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1441 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001442 synchronized (mLock) {
1443 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001444 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001445 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001446 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001447 }
1448 }
1449 }
1450 }
1451
Felipe Leme51e29da2017-10-24 14:03:10 -07001452 /** @hide */
1453 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1454 /** @hide */
1455 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1456 /** @hide */
1457 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1458 /** @hide */
1459 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1460 /** @hide */
1461 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1462
1463 private void setState(int flags) {
1464 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001465 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001466 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1467 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001468 // Reset the session state
1469 resetSessionLocked();
1470 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001471 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001472 // Reset connection to system
1473 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001474 if (mServiceClientCleaner != null) {
1475 mServiceClientCleaner.clean();
1476 mServiceClientCleaner = null;
1477 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001478 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001479 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001480 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1481 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001482 }
1483
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001484 /**
1485 * Sets a view as autofilled if the current value is the {code targetValue}.
1486 *
1487 * @param view The view that is to be autofilled
1488 * @param targetValue The value we want to fill into view
1489 */
1490 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1491 AutofillValue currentValue = view.getAutofillValue();
1492 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001493 synchronized (mLock) {
1494 if (mLastAutofilledData == null) {
1495 mLastAutofilledData = new ParcelableMap(1);
1496 }
1497 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001498 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001499 view.setAutofilled(true);
1500 }
1501 }
1502
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001503 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001504 synchronized (mLock) {
1505 if (sessionId != mSessionId) {
1506 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001507 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001508
Felipe Leme637e05e2017-12-06 12:09:37 -08001509 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001510 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001511 return;
1512 }
1513
1514 final int itemCount = ids.size();
1515 int numApplied = 0;
1516 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001517 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001518
1519 for (int i = 0; i < itemCount; i++) {
1520 final AutofillId id = ids.get(i);
1521 final AutofillValue value = values.get(i);
1522 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001523 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001524 if (view == null) {
1525 Log.w(TAG, "autofill(): no View with id " + viewId);
1526 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001527 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001528 if (id.isVirtual()) {
1529 if (virtualValues == null) {
1530 // Most likely there will be just one view with virtual children.
1531 virtualValues = new ArrayMap<>(1);
1532 }
1533 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1534 if (valuesByParent == null) {
1535 // We don't know the size yet, but usually it will be just a few fields...
1536 valuesByParent = new SparseArray<>(5);
1537 virtualValues.put(view, valuesByParent);
1538 }
1539 valuesByParent.put(id.getVirtualChildId(), value);
1540 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001541 // Mark the view as to be autofilled with 'value'
1542 if (mLastAutofilledData == null) {
1543 mLastAutofilledData = new ParcelableMap(itemCount - i);
1544 }
1545 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001546
1547 view.autofill(value);
1548
1549 // Set as autofilled if the values match now, e.g. when the value was updated
1550 // synchronously.
1551 // If autofill happens async, the view is set to autofilled in
1552 // notifyValueChanged.
1553 setAutofilledIfValuesIs(view, value);
1554
1555 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001556 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001557 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001558
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001559 if (virtualValues != null) {
1560 for (int i = 0; i < virtualValues.size(); i++) {
1561 final View parent = virtualValues.keyAt(i);
1562 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1563 parent.autofill(childrenValues);
1564 numApplied += childrenValues.size();
1565 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001566 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001567
Felipe Lemeb22d6352017-09-08 20:03:53 -07001568 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1569 .setPackageName(mContext.getPackageName())
1570 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1571 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001572 mMetricsLogger.write(log);
1573 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001574 }
1575
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001576 /**
1577 * Set the tracked views.
1578 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001579 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001580 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001581 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001582 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001583 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001584 */
Felipe Leme27e20222017-05-18 15:24:11 -07001585 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001586 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1587 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001588 synchronized (mLock) {
1589 if (mEnabled && mSessionId == sessionId) {
1590 if (saveOnAllViewsInvisible) {
1591 mTrackedViews = new TrackedViews(trackedIds);
1592 } else {
1593 mTrackedViews = null;
1594 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001595 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001596 if (fillableIds != null) {
1597 if (mFillableIds == null) {
1598 mFillableIds = new ArraySet<>(fillableIds.length);
1599 }
1600 for (AutofillId id : fillableIds) {
1601 mFillableIds.add(id);
1602 }
1603 if (sVerbose) {
1604 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1605 + ", mFillableIds" + mFillableIds);
1606 }
1607 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001608
1609 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1610 // Turn off trigger on previous view id.
1611 setNotifyOnClickLocked(mSaveTriggerId, false);
1612 }
1613
1614 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1615 // Turn on trigger on new view id.
1616 mSaveTriggerId = saveTriggerId;
1617 setNotifyOnClickLocked(mSaveTriggerId, true);
1618 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001619 }
1620 }
1621 }
1622
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001623 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1624 final View view = findView(id);
1625 if (view == null) {
1626 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1627 return;
1628 }
1629 view.setNotifyAutofillManagerOnClick(notify);
1630 }
1631
Felipe Lemec24a56a2017-08-03 14:27:57 -07001632 private void setSaveUiState(int sessionId, boolean shown) {
1633 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1634 synchronized (mLock) {
1635 if (mSessionId != NO_SESSION) {
1636 // Race condition: app triggered a new session after the previous session was
1637 // finished but before server called setSaveUiState() - need to cancel the new
1638 // session to avoid further inconsistent behavior.
1639 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1640 + ") called on existing session " + mSessionId + "; cancelling it");
1641 cancelSessionLocked();
1642 }
1643 if (shown) {
1644 mSessionId = sessionId;
1645 mState = STATE_SHOWING_SAVE_UI;
1646 } else {
1647 mSessionId = NO_SESSION;
1648 mState = STATE_UNKNOWN;
1649 }
1650 }
1651 }
1652
Felipe Leme650f7ab2017-09-19 13:08:24 -07001653 /**
1654 * Marks the state of the session as finished.
1655 *
1656 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001657 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1658 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1659 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001660 */
1661 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001662 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001663 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001664 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001665 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001666 }
1667 }
1668
Felipe Leme27e20222017-05-18 15:24:11 -07001669 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001670 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001671 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001672 if (anchor == null) {
1673 return;
1674 }
Felipe Leme27e20222017-05-18 15:24:11 -07001675 requestHideFillUi(id, anchor);
1676 }
1677
1678 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001679
1680 AutofillCallback callback = null;
1681 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001682 // We do not check the session id for two reasons:
1683 // 1. If local and remote session id are off sync the UI would be stuck shown
1684 // 2. There is a race between the user state being destroyed due the fill
1685 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001686 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001687 if (client != null) {
1688 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1689 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001690 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001691 }
1692 }
1693
1694 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001695 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001696 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001697 AutofillCallback.EVENT_INPUT_HIDDEN);
1698 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001699 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001700 }
1701 }
1702 }
1703
Felipe Leme17292d12017-10-24 14:03:10 -07001704 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001705 if (sVerbose) {
1706 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001707 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001708 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001709 final View anchor = findView(id);
1710 if (anchor == null) {
1711 return;
1712 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001713
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001714 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001715 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001716 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001717 callback = mCallback;
1718 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001719 }
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_UNAVAILABLE);
1725 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001726 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001727 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001728 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001729
Felipe Leme17292d12017-10-24 14:03:10 -07001730 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001731 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001732 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001733 }
1734 }
1735
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001736 /**
1737 * Get an array of viewIds from a List of {@link AutofillId}.
1738 *
1739 * @param autofillIds The autofill ids to convert
1740 *
1741 * @return The array of viewIds.
1742 */
Felipe Leme27e20222017-05-18 15:24:11 -07001743 // TODO: move to Helper as static method
1744 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1745 final int numIds = autofillIds.length;
1746 final int[] viewIds = new int[numIds];
1747 for (int i = 0; i < numIds; i++) {
1748 viewIds[i] = autofillIds[i].getViewId();
1749 }
1750
1751 return viewIds;
1752 }
1753
1754 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001755 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1756 final int numIds = autofillIds.size();
1757 final int[] viewIds = new int[numIds];
1758 for (int i = 0; i < numIds; i++) {
1759 viewIds[i] = autofillIds.get(i).getViewId();
1760 }
1761
1762 return viewIds;
1763 }
1764
1765 /**
1766 * Find a single view by its id.
1767 *
1768 * @param autofillId The autofill id of the view
1769 *
1770 * @return The view or {@code null} if view was not found
1771 */
1772 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001773 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001774
1775 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001776 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001777 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001778
Phil Weaver846cda932017-06-15 10:10:06 -07001779 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001780 }
1781
Felipe Lemebb810922017-04-25 15:54:06 -07001782 /** @hide */
1783 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001784 return mService != null;
1785 }
1786
Felipe Lemec24a56a2017-08-03 14:27:57 -07001787 /** @hide */
1788 public void onPendingSaveUi(int operation, IBinder token) {
1789 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1790
1791 synchronized (mLock) {
1792 try {
1793 mService.onPendingSaveUi(operation, token);
1794 } catch (RemoteException e) {
1795 e.rethrowFromSystemServer();
1796 }
1797 }
1798 }
1799
1800 /** @hide */
1801 public void dump(String outerPrefix, PrintWriter pw) {
1802 pw.print(outerPrefix); pw.println("AutofillManager:");
1803 final String pfx = outerPrefix + " ";
1804 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001805 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001806 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001807 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001808 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1809 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1810 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1811 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1812 pw.print(pfx); pw.print("tracked views: ");
1813 if (mTrackedViews == null) {
1814 pw.println("null");
1815 } else {
1816 final String pfx2 = pfx + " ";
1817 pw.println();
1818 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1819 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1820 }
1821 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001822 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1823 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme51e29da2017-10-24 14:03:10 -07001824 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1825 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001826 }
1827
Felipe Lemec7b45292017-09-19 09:06:20 -07001828 private String getStateAsStringLocked() {
1829 switch (mState) {
1830 case STATE_UNKNOWN:
1831 return "STATE_UNKNOWN";
1832 case STATE_ACTIVE:
1833 return "STATE_ACTIVE";
1834 case STATE_FINISHED:
1835 return "STATE_FINISHED";
1836 case STATE_SHOWING_SAVE_UI:
1837 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001838 case STATE_DISABLED_BY_SERVICE:
1839 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001840 default:
1841 return "INVALID:" + mState;
1842 }
1843 }
1844
Felipe Lemec24a56a2017-08-03 14:27:57 -07001845 private boolean isActiveLocked() {
1846 return mState == STATE_ACTIVE;
1847 }
1848
Felipe Leme3103c632017-12-18 15:05:14 -08001849 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07001850 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001851 }
1852
Felipe Leme3103c632017-12-18 15:05:14 -08001853 private boolean isFinishedLocked() {
1854 return mState == STATE_FINISHED;
1855 }
1856
Felipe Leme9876a6f2017-05-30 15:47:28 -07001857 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001858 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07001859 if (client == null) {
1860 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1861 return;
1862 }
1863 client.runOnUiThread(runnable);
1864 }
1865
Felipe Lemee6010f22017-03-03 11:19:51 -08001866 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001867 * View tracking information. Once all tracked views become invisible the session is finished.
1868 */
1869 private class TrackedViews {
1870 /** Visible tracked views */
1871 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1872
1873 /** Invisible tracked views */
1874 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1875
1876 /**
1877 * Check if set is null or value is in set.
1878 *
1879 * @param set The set or null (== empty set)
1880 * @param value The value that might be in the set
1881 *
1882 * @return {@code true} iff set is not empty and value is in set
1883 */
Felipe Leme27e20222017-05-18 15:24:11 -07001884 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001885 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1886 return set != null && set.contains(value);
1887 }
1888
1889 /**
1890 * Add a value to a set. If set is null, create a new set.
1891 *
1892 * @param set The set or null (== empty set)
1893 * @param valueToAdd The value to add
1894 *
1895 * @return The set including the new value. If set was {@code null}, a set containing only
1896 * the new value.
1897 */
Felipe Leme27e20222017-05-18 15:24:11 -07001898 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001899 @NonNull
1900 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1901 if (set == null) {
1902 set = new ArraySet<>(1);
1903 }
1904
1905 set.add(valueToAdd);
1906
1907 return set;
1908 }
1909
1910 /**
1911 * Remove a value from a set.
1912 *
1913 * @param set The set or null (== empty set)
1914 * @param valueToRemove The value to remove
1915 *
1916 * @return The set without the removed value. {@code null} if set was null, or is empty
1917 * after removal.
1918 */
Felipe Leme27e20222017-05-18 15:24:11 -07001919 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001920 @Nullable
1921 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1922 if (set == null) {
1923 return null;
1924 }
1925
1926 set.remove(valueToRemove);
1927
1928 if (set.isEmpty()) {
1929 return null;
1930 }
1931
1932 return set;
1933 }
1934
1935 /**
1936 * Set the tracked views.
1937 *
1938 * @param trackedIds The views to be tracked
1939 */
Felipe Leme27e20222017-05-18 15:24:11 -07001940 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001941 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001942 if (trackedIds != null && client != null) {
1943 final boolean[] isVisible;
1944
1945 if (client.isVisibleForAutofill()) {
1946 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1947 } else {
1948 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001949 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001950 }
1951
Felipe Leme27e20222017-05-18 15:24:11 -07001952 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001953 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001954 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001955
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001956 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001957 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001958 } else {
1959 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1960 }
1961 }
1962 }
1963
Felipe Leme9f9ee252017-04-27 13:56:22 -07001964 if (sVerbose) {
1965 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001966 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1967 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1968 }
1969
1970 if (mVisibleTrackedIds == null) {
1971 finishSessionLocked();
1972 }
1973 }
1974
1975 /**
1976 * Called when a {@link View view's} visibility changes.
1977 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001978 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001979 * @param isVisible visible if the view is visible in the view hierarchy.
1980 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001981 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001982 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001983
Felipe Leme9f9ee252017-04-27 13:56:22 -07001984 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001985 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001986 + isVisible);
1987 }
1988
1989 if (client != null && client.isVisibleForAutofill()) {
1990 if (isVisible) {
1991 if (isInSet(mInvisibleTrackedIds, id)) {
1992 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1993 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1994 }
1995 } else {
1996 if (isInSet(mVisibleTrackedIds, id)) {
1997 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1998 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1999 }
2000 }
2001 }
2002
2003 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002004 if (sVerbose) {
2005 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2006 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002007 finishSessionLocked();
2008 }
2009 }
2010
2011 /**
2012 * Called once the client becomes visible.
2013 *
2014 * @see AutofillClient#isVisibleForAutofill()
2015 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002016 void onVisibleForAutofillLocked() {
2017 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002018 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002019 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002020 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2021 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2022 if (client != null) {
2023 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002024 final ArrayList<AutofillId> orderedInvisibleIds =
2025 new ArrayList<>(mInvisibleTrackedIds);
2026 final boolean[] isVisible = client.getViewVisibility(
2027 getViewIds(orderedInvisibleIds));
2028
2029 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2030 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2031 final AutofillId id = orderedInvisibleIds.get(i);
2032 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002033 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2034
Felipe Leme9f9ee252017-04-27 13:56:22 -07002035 if (sDebug) {
2036 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002037 }
2038 } else {
2039 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2040 }
2041 }
2042 }
2043
2044 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002045 final ArrayList<AutofillId> orderedVisibleIds =
2046 new ArrayList<>(mVisibleTrackedIds);
2047 final boolean[] isVisible = client.getViewVisibility(
2048 getViewIds(orderedVisibleIds));
2049
2050 final int numVisibleTrackedIds = orderedVisibleIds.size();
2051 for (int i = 0; i < numVisibleTrackedIds; i++) {
2052 final AutofillId id = orderedVisibleIds.get(i);
2053
2054 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002055 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2056 } else {
2057 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2058
Felipe Leme9f9ee252017-04-27 13:56:22 -07002059 if (sDebug) {
2060 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002061 }
2062 }
2063 }
2064 }
2065
2066 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2067 mVisibleTrackedIds = updatedVisibleTrackedIds;
2068 }
2069
2070 if (mVisibleTrackedIds == null) {
2071 finishSessionLocked();
2072 }
2073 }
2074 }
2075
2076 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002077 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002078 *
2079 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002080 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002081 */
2082 public abstract static class AutofillCallback {
2083
2084 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002085 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2086 EVENT_INPUT_SHOWN,
2087 EVENT_INPUT_HIDDEN,
2088 EVENT_INPUT_UNAVAILABLE
2089 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002090 @Retention(RetentionPolicy.SOURCE)
2091 public @interface AutofillEventType {}
2092
2093 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002094 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002095 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002096 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002097 * should be hidden upon receiving this event.
2098 */
2099 public static final int EVENT_INPUT_SHOWN = 1;
2100
2101 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002102 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002103 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002104 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002105 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2106 */
2107 public static final int EVENT_INPUT_HIDDEN = 2;
2108
2109 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002110 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002111 * autofill is not available.
2112 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002113 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002114 * to avoid flickering, it could shown it upon receiving this event.
2115 */
2116 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2117
2118 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002119 * Called after a change in the autofill state associated with a view.
2120 *
2121 * @param view view associated with the change.
2122 *
2123 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2124 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002125 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2126 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002127
2128 /**
2129 * Called after a change in the autofill state associated with a virtual view.
2130 *
2131 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002132 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002133 *
2134 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2135 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002136 public void onAutofillEvent(@NonNull View view, int virtualId,
2137 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002138 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002139 }
2140
Felipe Leme640f30a2017-03-06 15:44:06 -08002141 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2142 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002143
Felipe Leme640f30a2017-03-06 15:44:06 -08002144 AutofillManagerClient(AutofillManager autofillManager) {
2145 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002146 }
2147
2148 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002149 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002150 final AutofillManager afm = mAfm.get();
2151 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002152 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002153 }
2154 }
2155
2156 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002157 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002158 final AutofillManager afm = mAfm.get();
2159 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002160 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002161 }
2162 }
2163
2164 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002165 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2166 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002167 final AutofillManager afm = mAfm.get();
2168 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002169 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002170 }
2171 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002172
2173 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002174 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2175 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002176 final AutofillManager afm = mAfm.get();
2177 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002178 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2179 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002180 }
2181 }
2182
2183 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002184 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002185 final AutofillManager afm = mAfm.get();
2186 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002187 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002188 }
2189 }
2190
2191 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002192 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002193 final AutofillManager afm = mAfm.get();
2194 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002195 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002196 }
2197 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002198
2199 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002200 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002201 final AutofillManager afm = mAfm.get();
2202 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002203 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002204 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002205 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002206 } catch (IntentSender.SendIntentException e) {
2207 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2208 }
2209 });
2210 }
2211 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002212
2213 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002214 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002215 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2216 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002217 final AutofillManager afm = mAfm.get();
2218 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002219 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2220 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002221 }
2222 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002223
2224 @Override
2225 public void setSaveUiState(int sessionId, boolean shown) {
2226 final AutofillManager afm = mAfm.get();
2227 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002228 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2229 }
2230 }
2231
2232 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002233 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002234 final AutofillManager afm = mAfm.get();
2235 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002236 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002237 }
2238 }
Svet Ganov782043c2017-02-11 00:52:02 +00002239 }
Felipe Lemebc055b02018-01-05 17:04:10 -08002240
2241 private abstract static class SyncRemoteCallbackListener<T>
2242 implements RemoteCallback.OnResultListener {
2243
2244 private final CountDownLatch mLatch = new CountDownLatch(1);
2245 private T mResult;
2246
2247 @Override
2248 public void onResult(Bundle result) {
2249 if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener.onResult(): " + result);
2250 mResult = getResult(result);
2251 mLatch.countDown();
2252 }
2253
2254 T getResult(int timeoutMs) {
2255 T result = null;
2256 try {
2257 if (mLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
2258 result = mResult;
2259 } else {
2260 Log.w(TAG, "SyncRemoteCallbackListener not called in " + timeoutMs + "ms");
2261 }
2262 } catch (InterruptedException e) {
2263 Log.w(TAG, "SyncRemoteCallbackListener interrupted: " + e);
2264 Thread.currentThread().interrupt();
2265 }
2266 if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener: returning " + result);
2267 return result;
2268 }
2269
2270 abstract T getResult(Bundle result);
2271 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002272}