blob: 27eeb2e67fb7b28e92c9411037ab8816c9cf0e77 [file] [log] [blame]
Felipe Leme3461d3c2017-01-19 08:54:55 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view.autofill;
18
Felipe Leme73fedac2017-05-12 09:52:07 -070019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
Felipe Leme9f9ee252017-04-27 13:56:22 -070020import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
Felipe Lemed633f072017-02-14 10:17:17 -080022
Felipe Lemee6010f22017-03-03 11:19:51 -080023import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080024import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080025import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.SystemService;
Felipe Leme3461d3c2017-01-19 08:54:55 -080027import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000028import android.content.Intent;
29import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080030import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070031import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070033import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000034import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070036import android.service.autofill.AutofillService;
37import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070038import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070039import android.util.ArraySet;
Felipe Lemec24a56a2017-08-03 14:27:57 -070040import android.util.DebugUtils;
Felipe Leme3461d3c2017-01-19 08:54:55 -080041import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070042import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080043import android.view.View;
44
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070045import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070046import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070047import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme4753bb02017-03-22 20:24:00 -070048
Felipe Lemec24a56a2017-08-03 14:27:57 -070049import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000052import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070053import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000054import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070055import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000056
Felipe Leme3461d3c2017-01-19 08:54:55 -080057/**
Felipe Leme744976e2017-07-31 11:34:14 -070058 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
59 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070060 *
Felipe Leme744976e2017-07-31 11:34:14 -070061 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
62 * activity context; the autofill context is created when one of the following methods is called for
63 * the first time in an activity context, and the current user has an enabled autofill service:
64 *
65 * <ul>
66 * <li>{@link #notifyViewEntered(View)}
67 * <li>{@link #notifyViewEntered(View, int, Rect)}
68 * <li>{@link #requestAutofill(View)}
69 * </ul>
70 *
71 * <p>Tipically, the context is automatically created when the first view of the activity is
72 * focused because {@code View.onFocusChanged()} indirectly calls
73 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
74 * explicitly create it (for example, a custom view developer could offer a contextual menu action
75 * in a text-field view to let users manually request autofill).
76 *
77 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
78 * that represents the view hierarchy by calling
79 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
80 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
81 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
82 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
83 * the hierarchy.
84 *
85 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
86 * parses it looking for views that can be autofilled. If the service finds such views, it returns
87 * a data structure to the Android System containing the following optional info:
88 *
89 * <ul>
90 * <li>Datasets used to autofill subsets of views in the activity.
91 * <li>Id of views that the service can save their values for future autofilling.
92 * </ul>
93 *
94 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
95 * UI affordance associated with the view, when the view is focused on and is part of a dataset.
96 * The application can be notified when the affordance is shown by registering an
97 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
98 * selects a dataset from the affordance, all views present in the dataset are autofilled, through
99 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
100 *
101 * <p>When the service returns ids of savable views, the Android System keeps track of changes
102 * made to these views, so they can be used to determine if the autofill save UI is shown later.
103 *
104 * <p>The context is then finished when one of the following occurs:
105 *
106 * <ul>
107 * <li>{@link #commit()} is called or all savable views are gone.
108 * <li>{@link #cancel()} is called.
109 * </ul>
110 *
111 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
112 * shows a save UI affordance if the value of savable views have changed. If the user selects the
113 * option to Save, the current value of the views is then sent to the autofill service.
114 *
115 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800116 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600117@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800118public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800119
Felipe Leme640f30a2017-03-06 15:44:06 -0800120 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800121
Svet Ganov782043c2017-02-11 00:52:02 +0000122 /**
123 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700124 *
Svet Ganov782043c2017-02-11 00:52:02 +0000125 * <p>
126 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000127 */
128 public static final String EXTRA_ASSIST_STRUCTURE =
129 "android.view.autofill.extra.ASSIST_STRUCTURE";
130
131 /**
132 * Intent extra: The result of an authentication operation. It is
133 * either a fully populated {@link android.service.autofill.FillResponse}
134 * or a fully populated {@link android.service.autofill.Dataset} if
135 * a response or a dataset is being authenticated respectively.
136 *
137 * <p>
138 * Type: {@link android.service.autofill.FillResponse} or a
139 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000140 */
141 public static final String EXTRA_AUTHENTICATION_RESULT =
142 "android.view.autofill.extra.AUTHENTICATION_RESULT";
143
Felipe Leme7320ca92017-03-29 15:09:54 -0700144 /**
145 * Intent extra: The optional extras provided by the
146 * {@link android.service.autofill.AutofillService}.
147 *
148 * <p>For example, when the service responds to a {@link
149 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
150 * a {@code FillResponse} that requires authentication, the Intent that launches the
151 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700152 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700153 *
154 * <p>
155 * Type: {@link android.os.Bundle}
156 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700157 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600158 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700159
Felipe Lemec24a56a2017-08-03 14:27:57 -0700160
161 /** @hide */
162 public static final String EXTRA_RESTORE_SESSION_TOKEN =
163 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
164
165 private static final String SESSION_ID_TAG = "android:sessionId";
166 private static final String STATE_TAG = "android:state";
167 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
168
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700169
Felipe Leme0aa4c502017-04-26 12:36:01 -0700170 /** @hide */ public static final int ACTION_START_SESSION = 1;
171 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
172 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
173 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800174
Felipe Leme9f9ee252017-04-27 13:56:22 -0700175
176 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
177 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
178 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
179
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700180 /** Which bits in an authentication id are used for the dataset id */
181 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
182 /** How many bits in an authentication id are used for the dataset id */
183 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
184 /** @hide The index for an undefined data set */
185 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
186
187 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700188 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
189 *
190 * @hide
191 */
192 public static final int PENDING_UI_OPERATION_CANCEL = 1;
193
194 /**
195 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
196 *
197 * @hide
198 */
199 public static final int PENDING_UI_OPERATION_RESTORE = 2;
200
201 /**
202 * Initial state of the autofill context, set when there is no session (i.e., when
203 * {@link #mSessionId} is {@link #NO_SESSION}).
204 *
205 * @hide
206 */
207 public static final int STATE_UNKNOWN = 1;
208
209 /**
210 * State where the autofill context hasn't been {@link #commit() finished} nor
211 * {@link #cancel() canceled} yet.
212 *
213 * @hide
214 */
215 public static final int STATE_ACTIVE = 2;
216
217 /**
218 * State where the autofill context has been {@link #commit() finished} but the server still has
219 * a session because the Save UI hasn't been dismissed yet.
220 *
221 * @hide
222 */
223 public static final int STATE_SHOWING_SAVE_UI = 4;
224
225 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700226 * Makes an authentication id from a request id and a dataset id.
227 *
228 * @param requestId The request id.
229 * @param datasetId The dataset id.
230 * @return The authentication id.
231 * @hide
232 */
233 public static int makeAuthenticationId(int requestId, int datasetId) {
234 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
235 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
236 }
237
238 /**
239 * Gets the request id from an authentication id.
240 *
241 * @param authRequestId The authentication id.
242 * @return The request id.
243 * @hide
244 */
245 public static int getRequestIdFromAuthenticationId(int authRequestId) {
246 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
247 }
248
249 /**
250 * Gets the dataset id from an authentication id.
251 *
252 * @param authRequestId The authentication id.
253 * @return The dataset id.
254 * @hide
255 */
256 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
257 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
258 }
259
Felipe Leme4753bb02017-03-22 20:24:00 -0700260 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000261
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700262 /**
263 * There is currently no session running.
264 * {@hide}
265 */
266 public static final int NO_SESSION = Integer.MIN_VALUE;
267
Svet Ganov782043c2017-02-11 00:52:02 +0000268 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700269
270 private final Object mLock = new Object();
271
272 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000273 private IAutoFillManagerClient mServiceClient;
274
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700275 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800276 private AutofillCallback mCallback;
277
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700278 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000279
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700280 @GuardedBy("mLock")
281 private int mSessionId = NO_SESSION;
282
283 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700284 private int mState = STATE_UNKNOWN;
285
286 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000287 private boolean mEnabled;
288
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700289 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700290 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700291 @Nullable private ParcelableMap mLastAutofilledData;
292
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700293 /** If view tracking is enabled, contains the tracking state */
294 @GuardedBy("mLock")
295 @Nullable private TrackedViews mTrackedViews;
296
Felipe Leme27e20222017-05-18 15:24:11 -0700297 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
298 @GuardedBy("mLock")
299 @Nullable private ArraySet<AutofillId> mFillableIds;
300
Svet Ganov782043c2017-02-11 00:52:02 +0000301 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800302 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000303 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000304 * Asks the client to start an authentication flow.
305 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700306 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000307 * @param intent The authentication intent.
308 * @param fillInIntent The authentication fill-in intent.
309 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700310 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
311 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800312
313 /**
314 * Tells the client this manager has state to be reset.
315 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700316 void autofillCallbackResetableStateAvailable();
317
318 /**
319 * Request showing the autofill UI.
320 *
321 * @param anchor The real view the UI needs to anchor to.
322 * @param width The width of the fill UI content.
323 * @param height The height of the fill UI content.
324 * @param virtualBounds The bounds of the virtual decendant of the anchor.
325 * @param presenter The presenter that controls the fill UI window.
326 * @return Whether the UI was shown.
327 */
328 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
329 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
330
331 /**
332 * Request hiding the autofill UI.
333 *
334 * @return Whether the UI was hidden.
335 */
336 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700337
338 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700339 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700340 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700341 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700342 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700343 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700344
345 /**
346 * Checks is the client is currently visible as understood by autofill.
347 *
348 * @return {@code true} if the client is currently visible
349 */
350 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700351
352 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700353 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700354 *
Phil Weaver846cda932017-06-15 10:10:06 -0700355 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700356 *
Felipe Leme27e20222017-05-18 15:24:11 -0700357 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700358 */
Phil Weaver846cda932017-06-15 10:10:06 -0700359 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700360
361 /**
362 * Finds a view by traversing the hierarchies of the client.
363 *
Phil Weaver846cda932017-06-15 10:10:06 -0700364 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700365 *
366 * @return The view, or {@code null} if not found
367 */
Phil Weaver846cda932017-06-15 10:10:06 -0700368 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700369
370 /**
371 * Runs the specified action on the UI thread.
372 */
373 void runOnUiThread(Runnable action);
Svet Ganov782043c2017-02-11 00:52:02 +0000374 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800375
376 /**
377 * @hide
378 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800379 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800380 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800381 mService = service;
382 }
383
384 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700385 * Restore state after activity lifecycle
386 *
387 * @param savedInstanceState The state to be restored
388 *
389 * {@hide}
390 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700391 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700392 if (!hasAutofillFeature()) {
393 return;
394 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700395 synchronized (mLock) {
396 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
397
Felipe Lemec24a56a2017-08-03 14:27:57 -0700398 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700399 Log.w(TAG, "New session was started before onCreate()");
400 return;
401 }
402
403 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700404 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700405
406 if (mSessionId != NO_SESSION) {
407 ensureServiceClientAddedIfNeededLocked();
408
409 final AutofillClient client = getClientLocked();
410 if (client != null) {
411 try {
412 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
413 mContext.getActivityToken(), mServiceClient.asBinder());
414
415 if (!sessionWasRestored) {
416 Log.w(TAG, "Session " + mSessionId + " could not be restored");
417 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700418 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700419 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700420 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700421 Log.d(TAG, "session " + mSessionId + " was restored");
422 }
423
424 client.autofillCallbackResetableStateAvailable();
425 }
426 } catch (RemoteException e) {
427 Log.e(TAG, "Could not figure out if there was an autofill session", e);
428 }
429 }
430 }
431 }
432 }
433
434 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700435 * Called once the client becomes visible.
436 *
437 * @see AutofillClient#isVisibleForAutofill()
438 *
439 * {@hide}
440 */
441 public void onVisibleForAutofill() {
442 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700443 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700444 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700445 }
446 }
447 }
448
449 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700450 * Save state before activity lifecycle
451 *
452 * @param outState Place to store the state
453 *
454 * {@hide}
455 */
456 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700457 if (!hasAutofillFeature()) {
458 return;
459 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700460 synchronized (mLock) {
461 if (mSessionId != NO_SESSION) {
462 outState.putInt(SESSION_ID_TAG, mSessionId);
463 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700464 if (mState != STATE_UNKNOWN) {
465 outState.putInt(STATE_TAG, mState);
466 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700467 if (mLastAutofilledData != null) {
468 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
469 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700470 }
471 }
472
473 /**
474 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700475 *
476 * <p>Typically used to determine whether the option to explicitly request autofill should
477 * be offered - see {@link #requestAutofill(View)}.
478 *
479 * @return whether autofill is enabled for the current user.
480 */
481 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700482 if (!hasAutofillFeature()) {
483 return false;
484 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700485 synchronized (mLock) {
486 ensureServiceClientAddedIfNeededLocked();
487 return mEnabled;
488 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700489 }
490
491 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700492 * Should always be called from {@link AutofillService#getFillEventHistory()}.
493 *
494 * @hide
495 */
496 @Nullable public FillEventHistory getFillEventHistory() {
497 try {
498 return mService.getFillEventHistory();
499 } catch (RemoteException e) {
500 e.rethrowFromSystemServer();
501 return null;
502 }
503 }
504
505 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700506 * Explicitly requests a new autofill context.
507 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700508 * <p>Normally, the autofill context is automatically started if necessary when
509 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
510 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
511 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700512 *
513 * @param view view requesting the new autofill context.
514 */
515 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700516 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700517 }
518
519 /**
520 * Explicitly requests a new autofill context for virtual views.
521 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700522 * <p>Normally, the autofill context is automatically started if necessary when
523 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
524 * cases where it must be explicitly started. For example, when the virtual view offers an
525 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700526 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700527 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
528 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
529 * the absolute bounds could be calculated by:
530 *
531 * <pre class="prettyprint">
532 * int offset[] = new int[2];
533 * getLocationOnScreen(offset);
534 * Rect absBounds = new Rect(bounds.left + offset[0],
535 * bounds.top + offset[1],
536 * bounds.right + offset[0], bounds.bottom + offset[1]);
537 * </pre>
538 *
539 * @param view the virtual view parent.
540 * @param virtualId id identifying the virtual child inside the parent view.
541 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700542 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700543 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
544 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700545 }
546
Felipe Leme2ac463e2017-03-13 14:06:25 -0700547 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700548 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800549 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700550 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800551 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700552 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700553 notifyViewEntered(view, 0);
554 }
555
556 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700557 if (!hasAutofillFeature()) {
558 return;
559 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700560 AutofillCallback callback = null;
561 synchronized (mLock) {
562 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000563
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700564 if (!mEnabled) {
565 if (mCallback != null) {
566 callback = mCallback;
567 }
568 } else {
569 final AutofillId id = getAutofillId(view);
570 final AutofillValue value = view.getAutofillValue();
571
Felipe Lemec24a56a2017-08-03 14:27:57 -0700572 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700573 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700574 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700575 } else {
576 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700577 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700578 }
Felipe Leme24aae152017-03-15 12:33:01 -0700579 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800580 }
581
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700582 if (callback != null) {
583 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000584 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800585 }
586
587 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700588 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800589 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700590 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800591 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700592 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700593 if (!hasAutofillFeature()) {
594 return;
595 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700596 synchronized (mLock) {
597 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800598
Felipe Lemec24a56a2017-08-03 14:27:57 -0700599 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700600 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800601
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700602 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700603 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700604 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800605 }
606 }
607
608 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700609 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700610 *
611 * @param view {@link View} that was exited.
612 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700613 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700614 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
615 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
616 }
617
618 /**
619 * Called when a virtual view's visibility changed.
620 *
621 * @param view {@link View} that was exited.
622 * @param virtualId id identifying the virtual child inside the parent view.
623 * @param isVisible visible if the view is visible in the view hierarchy.
624 */
625 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
626 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
627 }
628
629 /**
630 * Called when a view/virtual view's visibility changed.
631 *
632 * @param view {@link View} that was exited.
633 * @param virtualId id identifying the virtual child inside the parent view.
634 * @param isVisible visible if the view is visible in the view hierarchy.
635 * @param virtual Whether the view is virtual.
636 */
637 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
638 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700639 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700640 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700641 final AutofillId id = virtual ? getAutofillId(view, virtualId)
642 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700643 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700644 if (mFillableIds.contains(id)) {
645 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
646 requestHideFillUi(id, view);
647 }
648 }
649 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700650 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700651 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700652 }
653 }
654 }
655
656 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700657 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800658 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700659 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
660 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
661 * the absolute bounds could be calculated by:
662 *
663 * <pre class="prettyprint">
664 * int offset[] = new int[2];
665 * getLocationOnScreen(offset);
666 * Rect absBounds = new Rect(bounds.left + offset[0],
667 * bounds.top + offset[1],
668 * bounds.right + offset[0], bounds.bottom + offset[1]);
669 * </pre>
670 *
671 * @param view the virtual view parent.
672 * @param virtualId id identifying the virtual child inside the parent view.
673 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800674 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700675 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
676 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700677 }
678
Felipe Leme6dcec872017-05-25 11:24:23 -0700679 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700680 if (!hasAutofillFeature()) {
681 return;
682 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700683 AutofillCallback callback = null;
684 synchronized (mLock) {
685 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000686
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700687 if (!mEnabled) {
688 if (mCallback != null) {
689 callback = mCallback;
690 }
691 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700692 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700693
Felipe Lemec24a56a2017-08-03 14:27:57 -0700694 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700695 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700696 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700697 } else {
698 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700699 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700700 }
Felipe Leme24aae152017-03-15 12:33:01 -0700701 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800702 }
703
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700704 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700705 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700706 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800707 }
708 }
709
710 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700711 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800712 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700713 * @param view the virtual view parent.
714 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800715 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700716 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700717 if (!hasAutofillFeature()) {
718 return;
719 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700720 synchronized (mLock) {
721 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800722
Felipe Lemec24a56a2017-08-03 14:27:57 -0700723 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700724 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800725
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700726 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700727 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700728 }
Svet Ganov782043c2017-02-11 00:52:02 +0000729 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800730 }
731
732 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800733 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800734 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800735 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800736 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700737 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700738 if (!hasAutofillFeature()) {
739 return;
740 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700741 AutofillId id = null;
742 boolean valueWasRead = false;
743 AutofillValue value = null;
744
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700745 synchronized (mLock) {
746 // If the session is gone some fields might still be highlighted, hence we have to
747 // remove the isAutofilled property even if no sessions are active.
748 if (mLastAutofilledData == null) {
749 view.setAutofilled(false);
750 } else {
751 id = getAutofillId(view);
752 if (mLastAutofilledData.containsKey(id)) {
753 value = view.getAutofillValue();
754 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700755
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700756 if (Objects.equals(mLastAutofilledData.get(id), value)) {
757 view.setAutofilled(true);
758 } else {
759 view.setAutofilled(false);
760 mLastAutofilledData.remove(id);
761 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700762 } else {
763 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700764 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700765 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700766
Felipe Lemec24a56a2017-08-03 14:27:57 -0700767 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700768 return;
769 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800770
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700771 if (id == null) {
772 id = getAutofillId(view);
773 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700774
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700775 if (!valueWasRead) {
776 value = view.getAutofillValue();
777 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700778
Felipe Leme0aa4c502017-04-26 12:36:01 -0700779 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800781 }
782
Felipe Lemebab851c2017-02-03 18:45:08 -0800783 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700784 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800785 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700786 * @param view the virtual view parent.
787 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800788 * @param value new value of the child.
789 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700790 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700791 if (!hasAutofillFeature()) {
792 return;
793 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700794 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700795 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700796 return;
797 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800798
Felipe Leme6dcec872017-05-25 11:24:23 -0700799 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700800 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700801 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800802 }
803
804 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700805 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800806 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700807 * <p>This method is typically called by {@link View Views} that manage virtual views; for
808 * example, when the view is rendering an {@code HTML} page with a form and virtual views
809 * that represent the HTML elements, it should call this method after the form is submitted and
810 * another page is rendered.
811 *
812 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
813 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800814 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700815 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700816 if (!hasAutofillFeature()) {
817 return;
818 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700819 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700820 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700821 return;
822 }
Svet Ganov782043c2017-02-11 00:52:02 +0000823
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700824 finishSessionLocked();
825 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800826 }
827
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700828 /**
829 * Called to indicate the current autofill context should be cancelled.
830 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700831 * <p>This method is typically called by {@link View Views} that manage virtual views; for
832 * example, when the view is rendering an {@code HTML} page with a form and virtual views
833 * that represent the HTML elements, it should call this method if the user does not post the
834 * form but moves to another form in this page.
835 *
836 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
837 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700838 */
839 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700840 if (!hasAutofillFeature()) {
841 return;
842 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700843 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700844 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700845 return;
846 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700847
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700848 cancelSessionLocked();
849 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700850 }
851
Svet Ganovf965b872017-04-28 16:34:02 -0700852 /** @hide */
853 public void disableOwnedAutofillServices() {
854 disableAutofillServices();
855 }
856
Svet Ganovf20a0372017-04-10 17:08:05 -0700857 /**
858 * If the app calling this API has enabled autofill services they
859 * will be disabled.
860 */
Svet Ganovf965b872017-04-28 16:34:02 -0700861 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700862 if (!hasAutofillFeature()) {
863 return;
864 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700865 try {
866 mService.disableOwnedAutofillServices(mContext.getUserId());
867 } catch (RemoteException e) {
868 throw e.rethrowFromSystemServer();
869 }
870 }
871
Felipe Lemedb041182017-04-21 17:33:38 -0700872 /**
873 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
874 * enabled for the current user, or {@code false} otherwise.
875 */
876 public boolean hasEnabledAutofillServices() {
877 if (mService == null) return false;
878
879 try {
880 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
881 } catch (RemoteException e) {
882 throw e.rethrowFromSystemServer();
883 }
884 }
885
886 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -0700887 * Returns {@code true} if autofill is supported by the current device and
888 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -0700889 *
890 * <p>Autofill is typically supported, but it could be unsupported in cases like:
891 * <ol>
892 * <li>Low-end devices.
893 * <li>Device policy rules that forbid its usage.
894 * </ol>
895 */
896 public boolean isAutofillSupported() {
897 if (mService == null) return false;
898
899 try {
900 return mService.isServiceSupported(mContext.getUserId());
901 } catch (RemoteException e) {
902 throw e.rethrowFromSystemServer();
903 }
904 }
905
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700906 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800907 if (mContext instanceof AutofillClient) {
908 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000909 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800910 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000911 }
912
913 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700914 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700915 if (!hasAutofillFeature()) {
916 return;
917 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700918 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800919 // handling the cases where it's not RESULT_OK: it works fine if the service does not
920 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
921 // service set the extra and returned RESULT_CANCELED...
922
Felipe Leme9f9ee252017-04-27 13:56:22 -0700923 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800924
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700925 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700926 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700927 return;
928 }
929 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
930 final Bundle responseData = new Bundle();
931 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
932 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700933 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
934 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700935 } catch (RemoteException e) {
936 Log.e(TAG, "Error delivering authentication result", e);
937 }
Svet Ganov782043c2017-02-11 00:52:02 +0000938 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800939 }
940
Felipe Leme640f30a2017-03-06 15:44:06 -0800941 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -0700942 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800943 }
944
Felipe Leme6dcec872017-05-25 11:24:23 -0700945 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -0700946 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800947 }
948
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700949 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
950 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700951 if (sVerbose) {
952 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec24a56a2017-08-03 14:27:57 -0700953 + ", flags=" + flags + ", state=" + mState);
Felipe Lemebab851c2017-02-03 18:45:08 -0800954 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700955 if (mState != STATE_UNKNOWN) {
956 if (sDebug) Log.d(TAG, "not starting session for " + id + " on state " + mState);
957 return;
958 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800959 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700960 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -0800961 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800962 mCallback != null, flags, mContext.getOpPackageName());
Felipe Lemec24a56a2017-08-03 14:27:57 -0700963 if (mSessionId != NO_SESSION) {
964 mState = STATE_ACTIVE;
965 }
Felipe Leme0aa4c502017-04-26 12:36:01 -0700966 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800967 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700968 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800969 }
Svet Ganov782043c2017-02-11 00:52:02 +0000970 } catch (RemoteException e) {
971 throw e.rethrowFromSystemServer();
972 }
973 }
974
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700975 private void finishSessionLocked() {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700976 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + mState);
977
978 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700979
Svet Ganov782043c2017-02-11 00:52:02 +0000980 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700981 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800982 } catch (RemoteException e) {
983 throw e.rethrowFromSystemServer();
984 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700985
Felipe Lemec24a56a2017-08-03 14:27:57 -0700986 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -0800987 }
988
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700989 private void cancelSessionLocked() {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700990 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + mState);
991
992 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700993
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700994 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700995 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700996 } catch (RemoteException e) {
997 throw e.rethrowFromSystemServer();
998 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700999
Svet Ganov48f10a22017-04-26 18:49:30 -07001000 resetSessionLocked();
1001 }
1002
1003 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001004 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001005 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001006 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001007 mFillableIds = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001008 }
1009
Felipe Leme0aa4c502017-04-26 12:36:01 -07001010 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1011 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001012 if (sVerbose && action != ACTION_VIEW_EXITED) {
1013 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1014 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001015 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001016 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1017
Felipe Leme3461d3c2017-01-19 08:54:55 -08001018 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001019 if (restartIfNecessary) {
1020 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1021 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1022 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
1023 if (newId != mSessionId) {
1024 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1025 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001026 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme7f33cd32017-05-11 10:10:49 -07001027 final AutofillClient client = getClientLocked();
1028 if (client != null) {
1029 client.autofillCallbackResetableStateAvailable();
1030 }
1031 }
1032 } else {
1033 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1034 mContext.getUserId());
1035 }
1036
Felipe Leme3461d3c2017-01-19 08:54:55 -08001037 } catch (RemoteException e) {
1038 throw e.rethrowFromSystemServer();
1039 }
1040 }
Svet Ganov782043c2017-02-11 00:52:02 +00001041
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001042 private void ensureServiceClientAddedIfNeededLocked() {
1043 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001044 return;
1045 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001046
Svet Ganov782043c2017-02-11 00:52:02 +00001047 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001048 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001049 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001050 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
1051 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1052 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1053 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +00001054 } catch (RemoteException e) {
1055 throw e.rethrowFromSystemServer();
1056 }
1057 }
1058 }
1059
Felipe Lemee6010f22017-03-03 11:19:51 -08001060 /**
1061 * Registers a {@link AutofillCallback} to receive autofill events.
1062 *
1063 * @param callback callback to receive events.
1064 */
1065 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001066 if (!hasAutofillFeature()) {
1067 return;
1068 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001069 synchronized (mLock) {
1070 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001071
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001072 final boolean hadCallback = mCallback != null;
1073 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001074
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001075 if (!hadCallback) {
1076 try {
1077 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1078 } catch (RemoteException e) {
1079 throw e.rethrowFromSystemServer();
1080 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001081 }
1082 }
1083 }
1084
1085 /**
1086 * Unregisters a {@link AutofillCallback} to receive autofill events.
1087 *
1088 * @param callback callback to stop receiving events.
1089 */
1090 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001091 if (!hasAutofillFeature()) {
1092 return;
1093 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001094 synchronized (mLock) {
1095 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001096
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001097 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001098
Felipe Lemee6010f22017-03-03 11:19:51 -08001099 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001100 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001101 } catch (RemoteException e) {
1102 throw e.rethrowFromSystemServer();
1103 }
1104 }
1105 }
1106
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001107 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1108 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1109 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001110 if (anchor == null) {
1111 return;
1112 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001113
1114 AutofillCallback callback = null;
1115 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001116 if (mSessionId == sessionId) {
1117 AutofillClient client = getClientLocked();
1118
1119 if (client != null) {
1120 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1121 anchorBounds, presenter) && mCallback != null) {
1122 callback = mCallback;
1123 }
1124 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001125 }
1126 }
1127
1128 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001129 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001130 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001131 AutofillCallback.EVENT_INPUT_SHOWN);
1132 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001133 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001134 }
1135 }
1136 }
1137
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001138 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1139 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001140 synchronized (mLock) {
1141 if (sessionId == mSessionId) {
1142 AutofillClient client = getClientLocked();
1143 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001144 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001145 }
1146 }
1147 }
1148 }
1149
Svet Ganov48f10a22017-04-26 18:49:30 -07001150 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001151 synchronized (mLock) {
1152 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -07001153 if (!mEnabled || resetSession) {
1154 // Reset the session state
1155 resetSessionLocked();
1156 }
1157 if (resetClient) {
1158 // Reset connection to system
1159 mServiceClient = null;
1160 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001161 }
1162 }
1163
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001164 /**
1165 * Sets a view as autofilled if the current value is the {code targetValue}.
1166 *
1167 * @param view The view that is to be autofilled
1168 * @param targetValue The value we want to fill into view
1169 */
1170 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1171 AutofillValue currentValue = view.getAutofillValue();
1172 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001173 synchronized (mLock) {
1174 if (mLastAutofilledData == null) {
1175 mLastAutofilledData = new ParcelableMap(1);
1176 }
1177 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001178 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001179 view.setAutofilled(true);
1180 }
1181 }
1182
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001183 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001184 synchronized (mLock) {
1185 if (sessionId != mSessionId) {
1186 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001187 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001188
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001189 final AutofillClient client = getClientLocked();
1190 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001191 return;
1192 }
1193
1194 final int itemCount = ids.size();
1195 int numApplied = 0;
1196 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001197 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001198
1199 for (int i = 0; i < itemCount; i++) {
1200 final AutofillId id = ids.get(i);
1201 final AutofillValue value = values.get(i);
1202 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001203 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001204 if (view == null) {
1205 Log.w(TAG, "autofill(): no View with id " + viewId);
1206 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001207 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001208 if (id.isVirtual()) {
1209 if (virtualValues == null) {
1210 // Most likely there will be just one view with virtual children.
1211 virtualValues = new ArrayMap<>(1);
1212 }
1213 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1214 if (valuesByParent == null) {
1215 // We don't know the size yet, but usually it will be just a few fields...
1216 valuesByParent = new SparseArray<>(5);
1217 virtualValues.put(view, valuesByParent);
1218 }
1219 valuesByParent.put(id.getVirtualChildId(), value);
1220 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001221 // Mark the view as to be autofilled with 'value'
1222 if (mLastAutofilledData == null) {
1223 mLastAutofilledData = new ParcelableMap(itemCount - i);
1224 }
1225 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001226
1227 view.autofill(value);
1228
1229 // Set as autofilled if the values match now, e.g. when the value was updated
1230 // synchronously.
1231 // If autofill happens async, the view is set to autofilled in
1232 // notifyValueChanged.
1233 setAutofilledIfValuesIs(view, value);
1234
1235 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001236 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001237 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001238
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001239 if (virtualValues != null) {
1240 for (int i = 0; i < virtualValues.size(); i++) {
1241 final View parent = virtualValues.keyAt(i);
1242 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1243 parent.autofill(childrenValues);
1244 numApplied += childrenValues.size();
1245 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001246 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001247
Felipe Lemeb22d6352017-09-08 20:03:53 -07001248 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1249 .setPackageName(mContext.getPackageName())
1250 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1251 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001252 mMetricsLogger.write(log);
1253 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001254 }
1255
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001256 /**
1257 * Set the tracked views.
1258 *
1259 * @param trackedIds The views to be tracked
1260 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme27e20222017-05-18 15:24:11 -07001261 * @param fillableIds Views that might anchor FillUI.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001262 */
Felipe Leme27e20222017-05-18 15:24:11 -07001263 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1264 boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001265 synchronized (mLock) {
1266 if (mEnabled && mSessionId == sessionId) {
1267 if (saveOnAllViewsInvisible) {
1268 mTrackedViews = new TrackedViews(trackedIds);
1269 } else {
1270 mTrackedViews = null;
1271 }
Felipe Leme27e20222017-05-18 15:24:11 -07001272 if (fillableIds != null) {
1273 if (mFillableIds == null) {
1274 mFillableIds = new ArraySet<>(fillableIds.length);
1275 }
1276 for (AutofillId id : fillableIds) {
1277 mFillableIds.add(id);
1278 }
1279 if (sVerbose) {
1280 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1281 + ", mFillableIds" + mFillableIds);
1282 }
1283 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001284 }
1285 }
1286 }
1287
Felipe Lemec24a56a2017-08-03 14:27:57 -07001288 private void setSaveUiState(int sessionId, boolean shown) {
1289 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1290 synchronized (mLock) {
1291 if (mSessionId != NO_SESSION) {
1292 // Race condition: app triggered a new session after the previous session was
1293 // finished but before server called setSaveUiState() - need to cancel the new
1294 // session to avoid further inconsistent behavior.
1295 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1296 + ") called on existing session " + mSessionId + "; cancelling it");
1297 cancelSessionLocked();
1298 }
1299 if (shown) {
1300 mSessionId = sessionId;
1301 mState = STATE_SHOWING_SAVE_UI;
1302 } else {
1303 mSessionId = NO_SESSION;
1304 mState = STATE_UNKNOWN;
1305 }
1306 }
1307 }
1308
Felipe Leme27e20222017-05-18 15:24:11 -07001309 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001310 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001311 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001312 if (anchor == null) {
1313 return;
1314 }
Felipe Leme27e20222017-05-18 15:24:11 -07001315 requestHideFillUi(id, anchor);
1316 }
1317
1318 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001319
1320 AutofillCallback callback = null;
1321 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001322 // We do not check the session id for two reasons:
1323 // 1. If local and remote session id are off sync the UI would be stuck shown
1324 // 2. There is a race between the user state being destroyed due the fill
1325 // service being uninstalled and the UI being dismissed.
1326 AutofillClient client = getClientLocked();
1327 if (client != null) {
1328 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1329 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001330 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001331 }
1332 }
1333
1334 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001335 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001336 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001337 AutofillCallback.EVENT_INPUT_HIDDEN);
1338 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001339 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001340 }
1341 }
1342 }
1343
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001344 private void notifyNoFillUi(int sessionId, AutofillId id) {
1345 final View anchor = findView(id);
1346 if (anchor == null) {
1347 return;
1348 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001349
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001350 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001351 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001352 if (mSessionId == sessionId && getClientLocked() != null) {
1353 callback = mCallback;
1354 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001355 }
1356
1357 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001358 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001359 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001360 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1361 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001362 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001363 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001364
Felipe Leme4753bb02017-03-22 20:24:00 -07001365 }
1366 }
1367
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001368 /**
1369 * Get an array of viewIds from a List of {@link AutofillId}.
1370 *
1371 * @param autofillIds The autofill ids to convert
1372 *
1373 * @return The array of viewIds.
1374 */
Felipe Leme27e20222017-05-18 15:24:11 -07001375 // TODO: move to Helper as static method
1376 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1377 final int numIds = autofillIds.length;
1378 final int[] viewIds = new int[numIds];
1379 for (int i = 0; i < numIds; i++) {
1380 viewIds[i] = autofillIds[i].getViewId();
1381 }
1382
1383 return viewIds;
1384 }
1385
1386 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001387 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1388 final int numIds = autofillIds.size();
1389 final int[] viewIds = new int[numIds];
1390 for (int i = 0; i < numIds; i++) {
1391 viewIds[i] = autofillIds.get(i).getViewId();
1392 }
1393
1394 return viewIds;
1395 }
1396
1397 /**
1398 * Find a single view by its id.
1399 *
1400 * @param autofillId The autofill id of the view
1401 *
1402 * @return The view or {@code null} if view was not found
1403 */
1404 private View findView(@NonNull AutofillId autofillId) {
1405 final AutofillClient client = getClientLocked();
1406
1407 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001408 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001409 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001410
Phil Weaver846cda932017-06-15 10:10:06 -07001411 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001412 }
1413
Felipe Lemebb810922017-04-25 15:54:06 -07001414 /** @hide */
1415 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001416 return mService != null;
1417 }
1418
Felipe Lemec24a56a2017-08-03 14:27:57 -07001419 /** @hide */
1420 public void onPendingSaveUi(int operation, IBinder token) {
1421 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1422
1423 synchronized (mLock) {
1424 try {
1425 mService.onPendingSaveUi(operation, token);
1426 } catch (RemoteException e) {
1427 e.rethrowFromSystemServer();
1428 }
1429 }
1430 }
1431
1432 /** @hide */
1433 public void dump(String outerPrefix, PrintWriter pw) {
1434 pw.print(outerPrefix); pw.println("AutofillManager:");
1435 final String pfx = outerPrefix + " ";
1436 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
1437 pw.print(pfx); pw.print("state: "); pw.println(
1438 DebugUtils.flagsToString(AutofillManager.class, "STATE_", mState));
1439 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1440 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1441 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1442 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1443 pw.print(pfx); pw.print("tracked views: ");
1444 if (mTrackedViews == null) {
1445 pw.println("null");
1446 } else {
1447 final String pfx2 = pfx + " ";
1448 pw.println();
1449 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1450 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1451 }
1452 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
1453 }
1454
1455 private boolean isActiveLocked() {
1456 return mState == STATE_ACTIVE;
1457 }
1458
Felipe Leme9876a6f2017-05-30 15:47:28 -07001459 private void post(Runnable runnable) {
1460 final AutofillClient client = getClientLocked();
1461 if (client == null) {
1462 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1463 return;
1464 }
1465 client.runOnUiThread(runnable);
1466 }
1467
Felipe Lemee6010f22017-03-03 11:19:51 -08001468 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001469 * View tracking information. Once all tracked views become invisible the session is finished.
1470 */
1471 private class TrackedViews {
1472 /** Visible tracked views */
1473 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1474
1475 /** Invisible tracked views */
1476 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1477
1478 /**
1479 * Check if set is null or value is in set.
1480 *
1481 * @param set The set or null (== empty set)
1482 * @param value The value that might be in the set
1483 *
1484 * @return {@code true} iff set is not empty and value is in set
1485 */
Felipe Leme27e20222017-05-18 15:24:11 -07001486 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001487 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1488 return set != null && set.contains(value);
1489 }
1490
1491 /**
1492 * Add a value to a set. If set is null, create a new set.
1493 *
1494 * @param set The set or null (== empty set)
1495 * @param valueToAdd The value to add
1496 *
1497 * @return The set including the new value. If set was {@code null}, a set containing only
1498 * the new value.
1499 */
Felipe Leme27e20222017-05-18 15:24:11 -07001500 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001501 @NonNull
1502 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1503 if (set == null) {
1504 set = new ArraySet<>(1);
1505 }
1506
1507 set.add(valueToAdd);
1508
1509 return set;
1510 }
1511
1512 /**
1513 * Remove a value from a set.
1514 *
1515 * @param set The set or null (== empty set)
1516 * @param valueToRemove The value to remove
1517 *
1518 * @return The set without the removed value. {@code null} if set was null, or is empty
1519 * after removal.
1520 */
Felipe Leme27e20222017-05-18 15:24:11 -07001521 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001522 @Nullable
1523 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1524 if (set == null) {
1525 return null;
1526 }
1527
1528 set.remove(valueToRemove);
1529
1530 if (set.isEmpty()) {
1531 return null;
1532 }
1533
1534 return set;
1535 }
1536
1537 /**
1538 * Set the tracked views.
1539 *
1540 * @param trackedIds The views to be tracked
1541 */
Felipe Leme27e20222017-05-18 15:24:11 -07001542 TrackedViews(@Nullable AutofillId[] trackedIds) {
1543 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001544 if (trackedIds != null && client != null) {
1545 final boolean[] isVisible;
1546
1547 if (client.isVisibleForAutofill()) {
1548 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1549 } else {
1550 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001551 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001552 }
1553
Felipe Leme27e20222017-05-18 15:24:11 -07001554 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001555 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001556 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001557
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001558 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001559 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001560 } else {
1561 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1562 }
1563 }
1564 }
1565
Felipe Leme9f9ee252017-04-27 13:56:22 -07001566 if (sVerbose) {
1567 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001568 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1569 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1570 }
1571
1572 if (mVisibleTrackedIds == null) {
1573 finishSessionLocked();
1574 }
1575 }
1576
1577 /**
1578 * Called when a {@link View view's} visibility changes.
1579 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001580 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001581 * @param isVisible visible if the view is visible in the view hierarchy.
1582 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001583 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001584 AutofillClient client = getClientLocked();
1585
Felipe Leme9f9ee252017-04-27 13:56:22 -07001586 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001587 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001588 + isVisible);
1589 }
1590
1591 if (client != null && client.isVisibleForAutofill()) {
1592 if (isVisible) {
1593 if (isInSet(mInvisibleTrackedIds, id)) {
1594 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1595 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1596 }
1597 } else {
1598 if (isInSet(mVisibleTrackedIds, id)) {
1599 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1600 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1601 }
1602 }
1603 }
1604
1605 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001606 if (sVerbose) {
1607 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1608 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001609 finishSessionLocked();
1610 }
1611 }
1612
1613 /**
1614 * Called once the client becomes visible.
1615 *
1616 * @see AutofillClient#isVisibleForAutofill()
1617 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001618 void onVisibleForAutofillLocked() {
1619 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001620 // hence update the visibility state for all views.
1621 AutofillClient client = getClientLocked();
1622 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1623 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1624 if (client != null) {
1625 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001626 final ArrayList<AutofillId> orderedInvisibleIds =
1627 new ArrayList<>(mInvisibleTrackedIds);
1628 final boolean[] isVisible = client.getViewVisibility(
1629 getViewIds(orderedInvisibleIds));
1630
1631 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1632 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1633 final AutofillId id = orderedInvisibleIds.get(i);
1634 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001635 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1636
Felipe Leme9f9ee252017-04-27 13:56:22 -07001637 if (sDebug) {
1638 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001639 }
1640 } else {
1641 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1642 }
1643 }
1644 }
1645
1646 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001647 final ArrayList<AutofillId> orderedVisibleIds =
1648 new ArrayList<>(mVisibleTrackedIds);
1649 final boolean[] isVisible = client.getViewVisibility(
1650 getViewIds(orderedVisibleIds));
1651
1652 final int numVisibleTrackedIds = orderedVisibleIds.size();
1653 for (int i = 0; i < numVisibleTrackedIds; i++) {
1654 final AutofillId id = orderedVisibleIds.get(i);
1655
1656 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001657 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1658 } else {
1659 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1660
Felipe Leme9f9ee252017-04-27 13:56:22 -07001661 if (sDebug) {
1662 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001663 }
1664 }
1665 }
1666 }
1667
1668 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1669 mVisibleTrackedIds = updatedVisibleTrackedIds;
1670 }
1671
1672 if (mVisibleTrackedIds == null) {
1673 finishSessionLocked();
1674 }
1675 }
1676 }
1677
1678 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001679 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08001680 *
1681 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme744976e2017-07-31 11:34:14 -07001682 * enable / disable such views when the autofill UI affordance is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001683 */
1684 public abstract static class AutofillCallback {
1685
1686 /** @hide */
1687 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1688 @Retention(RetentionPolicy.SOURCE)
1689 public @interface AutofillEventType {}
1690
1691 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001692 * The autofill input UI affordance associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08001693 *
1694 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1695 * should be hidden upon receiving this event.
1696 */
1697 public static final int EVENT_INPUT_SHOWN = 1;
1698
1699 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001700 * The autofill input UI affordance associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001701 *
1702 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1703 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1704 */
1705 public static final int EVENT_INPUT_HIDDEN = 2;
1706
1707 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001708 * The autofill input UI affordance associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07001709 * autofill is not available.
1710 *
1711 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1712 * to avoid flickering, it could shown it upon receiving this event.
1713 */
1714 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1715
1716 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001717 * Called after a change in the autofill state associated with a view.
1718 *
1719 * @param view view associated with the change.
1720 *
1721 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1722 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001723 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1724 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001725
1726 /**
1727 * Called after a change in the autofill state associated with a virtual view.
1728 *
1729 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07001730 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08001731 *
1732 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1733 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001734 public void onAutofillEvent(@NonNull View view, int virtualId,
1735 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07001736 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001737 }
1738
Felipe Leme640f30a2017-03-06 15:44:06 -08001739 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1740 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001741
Felipe Leme640f30a2017-03-06 15:44:06 -08001742 AutofillManagerClient(AutofillManager autofillManager) {
1743 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001744 }
1745
1746 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001747 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001748 final AutofillManager afm = mAfm.get();
1749 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001750 afm.post(() -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001751 }
1752 }
1753
1754 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001755 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001756 final AutofillManager afm = mAfm.get();
1757 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001758 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001759 }
1760 }
1761
1762 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001763 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1764 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001765 final AutofillManager afm = mAfm.get();
1766 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001767 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001768 }
1769 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001770
1771 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001772 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1773 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001774 final AutofillManager afm = mAfm.get();
1775 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001776 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1777 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001778 }
1779 }
1780
1781 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001782 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001783 final AutofillManager afm = mAfm.get();
1784 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001785 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001786 }
1787 }
1788
1789 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001790 public void notifyNoFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001791 final AutofillManager afm = mAfm.get();
1792 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001793 afm.post(() -> afm.notifyNoFillUi(sessionId, id));
Felipe Lemee6010f22017-03-03 11:19:51 -08001794 }
1795 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001796
1797 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07001798 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001799 final AutofillManager afm = mAfm.get();
1800 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001801 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001802 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001803 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07001804 } catch (IntentSender.SendIntentException e) {
1805 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1806 }
1807 });
1808 }
1809 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001810
1811 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07001812 public void setTrackedViews(int sessionId, AutofillId[] ids,
1813 boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001814 final AutofillManager afm = mAfm.get();
1815 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001816 afm.post(() ->
Felipe Leme27e20222017-05-18 15:24:11 -07001817 afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001818 );
1819 }
1820 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001821
1822 @Override
1823 public void setSaveUiState(int sessionId, boolean shown) {
1824 final AutofillManager afm = mAfm.get();
1825 if (afm != null) {
1826 afm.post(() ->afm.setSaveUiState(sessionId, shown));
1827 }
1828 }
Svet Ganov782043c2017-02-11 00:52:02 +00001829 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001830}