blob: 78b41c6f4c7bed463530e7022fd2af88cd60a2ca [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 Leme3461d3c2017-01-19 08:54:55 -080036import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070037import android.service.autofill.AutofillService;
38import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080039import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070040import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070041import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080042import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070043import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080044import android.view.View;
45
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070046import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070047import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070048import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme637e05e2017-12-06 12:09:37 -080049import com.android.internal.util.Preconditions;
Felipe Leme4753bb02017-03-22 20:24:00 -070050
Felipe Lemec24a56a2017-08-03 14:27:57 -070051import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080052import java.lang.annotation.Retention;
53import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000054import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070055import java.util.ArrayList;
Felipe Leme27f45732017-12-22 09:05:22 -080056import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000057import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070058import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000059
Koji Fukuiccec6a62017-10-18 17:48:40 +090060// TODO: use java.lang.ref.Cleaner once Android supports Java 9
61import sun.misc.Cleaner;
62
Felipe Leme3461d3c2017-01-19 08:54:55 -080063/**
Felipe Leme744976e2017-07-31 11:34:14 -070064 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
65 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070066 *
Felipe Leme744976e2017-07-31 11:34:14 -070067 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
68 * activity context; the autofill context is created when one of the following methods is called for
69 * the first time in an activity context, and the current user has an enabled autofill service:
70 *
71 * <ul>
72 * <li>{@link #notifyViewEntered(View)}
73 * <li>{@link #notifyViewEntered(View, int, Rect)}
74 * <li>{@link #requestAutofill(View)}
75 * </ul>
76 *
77 * <p>Tipically, the context is automatically created when the first view of the activity is
78 * focused because {@code View.onFocusChanged()} indirectly calls
79 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
80 * explicitly create it (for example, a custom view developer could offer a contextual menu action
81 * in a text-field view to let users manually request autofill).
82 *
83 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
84 * that represents the view hierarchy by calling
85 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
86 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
87 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
88 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
89 * the hierarchy.
90 *
91 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
92 * parses it looking for views that can be autofilled. If the service finds such views, it returns
93 * a data structure to the Android System containing the following optional info:
94 *
95 * <ul>
96 * <li>Datasets used to autofill subsets of views in the activity.
97 * <li>Id of views that the service can save their values for future autofilling.
98 * </ul>
99 *
100 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700101 * UI associated with the view, when the view is focused on and is part of a dataset.
102 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700103 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700104 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700105 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
106 *
107 * <p>When the service returns ids of savable views, the Android System keeps track of changes
108 * made to these views, so they can be used to determine if the autofill save UI is shown later.
109 *
110 * <p>The context is then finished when one of the following occurs:
111 *
112 * <ul>
113 * <li>{@link #commit()} is called or all savable views are gone.
114 * <li>{@link #cancel()} is called.
115 * </ul>
116 *
117 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700118 * 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 -0700119 * option to Save, the current value of the views is then sent to the autofill service.
120 *
121 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800122 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600123@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800124public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800125
Felipe Leme640f30a2017-03-06 15:44:06 -0800126 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800127
Svet Ganov782043c2017-02-11 00:52:02 +0000128 /**
129 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700130 *
Svet Ganov782043c2017-02-11 00:52:02 +0000131 * <p>
132 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000133 */
134 public static final String EXTRA_ASSIST_STRUCTURE =
135 "android.view.autofill.extra.ASSIST_STRUCTURE";
136
137 /**
138 * Intent extra: The result of an authentication operation. It is
139 * either a fully populated {@link android.service.autofill.FillResponse}
140 * or a fully populated {@link android.service.autofill.Dataset} if
141 * a response or a dataset is being authenticated respectively.
142 *
143 * <p>
144 * Type: {@link android.service.autofill.FillResponse} or a
145 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000146 */
147 public static final String EXTRA_AUTHENTICATION_RESULT =
148 "android.view.autofill.extra.AUTHENTICATION_RESULT";
149
Felipe Leme7320ca92017-03-29 15:09:54 -0700150 /**
151 * Intent extra: The optional extras provided by the
152 * {@link android.service.autofill.AutofillService}.
153 *
154 * <p>For example, when the service responds to a {@link
155 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
156 * a {@code FillResponse} that requires authentication, the Intent that launches the
157 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700158 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700159 *
Felipe Lemea9372382017-10-09 14:42:36 -0700160 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
161 * can also add this bundle to the {@link Intent} set as the
162 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
163 * so the bundle can be recovered later on
164 * {@link android.service.autofill.SaveRequest#getClientState()}.
165 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700166 * <p>
167 * Type: {@link android.os.Bundle}
168 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700169 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600170 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700171
Felipe Lemec24a56a2017-08-03 14:27:57 -0700172
173 /** @hide */
174 public static final String EXTRA_RESTORE_SESSION_TOKEN =
175 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
176
177 private static final String SESSION_ID_TAG = "android:sessionId";
178 private static final String STATE_TAG = "android:state";
179 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
180
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700181
Felipe Leme0aa4c502017-04-26 12:36:01 -0700182 /** @hide */ public static final int ACTION_START_SESSION = 1;
183 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
184 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
185 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800186
Felipe Leme9f9ee252017-04-27 13:56:22 -0700187
188 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
189 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
190 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
191
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700192 /** Which bits in an authentication id are used for the dataset id */
193 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
194 /** How many bits in an authentication id are used for the dataset id */
195 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
196 /** @hide The index for an undefined data set */
197 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
198
199 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700200 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
201 *
202 * @hide
203 */
204 public static final int PENDING_UI_OPERATION_CANCEL = 1;
205
206 /**
207 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
208 *
209 * @hide
210 */
211 public static final int PENDING_UI_OPERATION_RESTORE = 2;
212
213 /**
214 * Initial state of the autofill context, set when there is no session (i.e., when
215 * {@link #mSessionId} is {@link #NO_SESSION}).
216 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700217 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
218 * the server.
219 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700220 * @hide
221 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700222 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700223
224 /**
225 * State where the autofill context hasn't been {@link #commit() finished} nor
226 * {@link #cancel() canceled} yet.
227 *
228 * @hide
229 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700230 public static final int STATE_ACTIVE = 1;
231
232 /**
233 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700234 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700235 *
236 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
237 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
238 *
239 * @hide
240 */
241 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700242
243 /**
244 * State where the autofill context has been {@link #commit() finished} but the server still has
245 * a session because the Save UI hasn't been dismissed yet.
246 *
247 * @hide
248 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700249 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700250
251 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700252 * State where the autofill is disabled because the service cannot autofill the activity at all.
253 *
254 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
255 * (and {@link #requestAutofill(View, int, Rect)}).
256 *
257 * @hide
258 */
259 public static final int STATE_DISABLED_BY_SERVICE = 4;
260
261 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700262 * Makes an authentication id from a request id and a dataset id.
263 *
264 * @param requestId The request id.
265 * @param datasetId The dataset id.
266 * @return The authentication id.
267 * @hide
268 */
269 public static int makeAuthenticationId(int requestId, int datasetId) {
270 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
271 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
272 }
273
274 /**
275 * Gets the request id from an authentication id.
276 *
277 * @param authRequestId The authentication id.
278 * @return The request id.
279 * @hide
280 */
281 public static int getRequestIdFromAuthenticationId(int authRequestId) {
282 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
283 }
284
285 /**
286 * Gets the dataset id from an authentication id.
287 *
288 * @param authRequestId The authentication id.
289 * @return The dataset id.
290 * @hide
291 */
292 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
293 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
294 }
295
Felipe Leme4753bb02017-03-22 20:24:00 -0700296 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000297
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700298 /**
299 * There is currently no session running.
300 * {@hide}
301 */
302 public static final int NO_SESSION = Integer.MIN_VALUE;
303
Svet Ganov782043c2017-02-11 00:52:02 +0000304 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700305
306 private final Object mLock = new Object();
307
308 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000309 private IAutoFillManagerClient mServiceClient;
310
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700311 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900312 private Cleaner mServiceClientCleaner;
313
314 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800315 private AutofillCallback mCallback;
316
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700317 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000318
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700319 @GuardedBy("mLock")
320 private int mSessionId = NO_SESSION;
321
322 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700323 private int mState = STATE_UNKNOWN;
324
325 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000326 private boolean mEnabled;
327
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700328 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700329 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700330 @Nullable private ParcelableMap mLastAutofilledData;
331
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700332 /** If view tracking is enabled, contains the tracking state */
333 @GuardedBy("mLock")
334 @Nullable private TrackedViews mTrackedViews;
335
Felipe Leme27e20222017-05-18 15:24:11 -0700336 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
337 @GuardedBy("mLock")
338 @Nullable private ArraySet<AutofillId> mFillableIds;
339
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700340 /** If set, session is commited when the field is clicked. */
341 @GuardedBy("mLock")
342 @Nullable private AutofillId mSaveTriggerId;
343
344 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
345 @GuardedBy("mLock")
346 private boolean mSaveOnFinish;
347
Svet Ganov782043c2017-02-11 00:52:02 +0000348 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800349 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000350 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000351 * Asks the client to start an authentication flow.
352 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700353 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000354 * @param intent The authentication intent.
355 * @param fillInIntent The authentication fill-in intent.
356 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700357 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
358 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800359
360 /**
361 * Tells the client this manager has state to be reset.
362 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700363 void autofillCallbackResetableStateAvailable();
364
365 /**
366 * Request showing the autofill UI.
367 *
368 * @param anchor The real view the UI needs to anchor to.
369 * @param width The width of the fill UI content.
370 * @param height The height of the fill UI content.
371 * @param virtualBounds The bounds of the virtual decendant of the anchor.
372 * @param presenter The presenter that controls the fill UI window.
373 * @return Whether the UI was shown.
374 */
375 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
376 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
377
378 /**
379 * Request hiding the autofill UI.
380 *
381 * @return Whether the UI was hidden.
382 */
383 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700384
385 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700386 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700387 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700388 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700389 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700390 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700391
392 /**
393 * Checks is the client is currently visible as understood by autofill.
394 *
395 * @return {@code true} if the client is currently visible
396 */
397 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700398
399 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700400 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700401 *
Phil Weaver846cda932017-06-15 10:10:06 -0700402 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700403 *
Felipe Leme27e20222017-05-18 15:24:11 -0700404 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700405 */
Phil Weaver846cda932017-06-15 10:10:06 -0700406 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700407
408 /**
409 * Finds a view by traversing the hierarchies of the client.
410 *
Phil Weaver846cda932017-06-15 10:10:06 -0700411 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700412 *
413 * @return The view, or {@code null} if not found
414 */
Phil Weaver846cda932017-06-15 10:10:06 -0700415 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700416
417 /**
418 * Runs the specified action on the UI thread.
419 */
420 void runOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700421
422 /**
423 * Gets the complete component name of this client.
424 */
425 ComponentName getComponentName();
Svet Ganov782043c2017-02-11 00:52:02 +0000426 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800427
428 /**
429 * @hide
430 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800431 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800432 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800433 mService = service;
434 }
435
436 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700437 * Restore state after activity lifecycle
438 *
439 * @param savedInstanceState The state to be restored
440 *
441 * {@hide}
442 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700443 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700444 if (!hasAutofillFeature()) {
445 return;
446 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700447 synchronized (mLock) {
448 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
449
Felipe Lemec24a56a2017-08-03 14:27:57 -0700450 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700451 Log.w(TAG, "New session was started before onCreate()");
452 return;
453 }
454
455 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700456 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700457
458 if (mSessionId != NO_SESSION) {
459 ensureServiceClientAddedIfNeededLocked();
460
Felipe Leme637e05e2017-12-06 12:09:37 -0800461 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700462 if (client != null) {
463 try {
464 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
465 mContext.getActivityToken(), mServiceClient.asBinder());
466
467 if (!sessionWasRestored) {
468 Log.w(TAG, "Session " + mSessionId + " could not be restored");
469 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700470 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700471 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700472 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700473 Log.d(TAG, "session " + mSessionId + " was restored");
474 }
475
476 client.autofillCallbackResetableStateAvailable();
477 }
478 } catch (RemoteException e) {
479 Log.e(TAG, "Could not figure out if there was an autofill session", e);
480 }
481 }
482 }
483 }
484 }
485
486 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700487 * Called once the client becomes visible.
488 *
489 * @see AutofillClient#isVisibleForAutofill()
490 *
491 * {@hide}
492 */
493 public void onVisibleForAutofill() {
494 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700495 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700496 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700497 }
498 }
499 }
500
501 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700502 * Save state before activity lifecycle
503 *
504 * @param outState Place to store the state
505 *
506 * {@hide}
507 */
508 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700509 if (!hasAutofillFeature()) {
510 return;
511 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700512 synchronized (mLock) {
513 if (mSessionId != NO_SESSION) {
514 outState.putInt(SESSION_ID_TAG, mSessionId);
515 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700516 if (mState != STATE_UNKNOWN) {
517 outState.putInt(STATE_TAG, mState);
518 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700519 if (mLastAutofilledData != null) {
520 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
521 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700522 }
523 }
524
525 /**
526 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700527 *
528 * <p>Typically used to determine whether the option to explicitly request autofill should
529 * be offered - see {@link #requestAutofill(View)}.
530 *
531 * @return whether autofill is enabled for the current user.
532 */
533 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800534 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700535 return false;
536 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700537 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800538 if (isDisabledByServiceLocked()) {
539 return false;
540 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700541 ensureServiceClientAddedIfNeededLocked();
542 return mEnabled;
543 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700544 }
545
546 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700547 * Should always be called from {@link AutofillService#getFillEventHistory()}.
548 *
549 * @hide
550 */
551 @Nullable public FillEventHistory getFillEventHistory() {
552 try {
553 return mService.getFillEventHistory();
554 } catch (RemoteException e) {
555 e.rethrowFromSystemServer();
556 return null;
557 }
558 }
559
560 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700561 * Explicitly requests a new autofill context.
562 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700563 * <p>Normally, the autofill context is automatically started if necessary when
564 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
565 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
566 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700567 *
568 * @param view view requesting the new autofill context.
569 */
570 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700571 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700572 }
573
574 /**
575 * Explicitly requests a new autofill context for virtual views.
576 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700577 * <p>Normally, the autofill context is automatically started if necessary when
578 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
579 * cases where it must be explicitly started. For example, when the virtual view offers an
580 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700581 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700582 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
583 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
584 * the absolute bounds could be calculated by:
585 *
586 * <pre class="prettyprint">
587 * int offset[] = new int[2];
588 * getLocationOnScreen(offset);
589 * Rect absBounds = new Rect(bounds.left + offset[0],
590 * bounds.top + offset[1],
591 * bounds.right + offset[0], bounds.bottom + offset[1]);
592 * </pre>
593 *
594 * @param view the virtual view parent.
595 * @param virtualId id identifying the virtual child inside the parent view.
596 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700597 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700598 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
599 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700600 }
601
Felipe Leme2ac463e2017-03-13 14:06:25 -0700602 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700603 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800604 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700605 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800606 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700607 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700608 notifyViewEntered(view, 0);
609 }
610
Felipe Leme17292d12017-10-24 14:03:10 -0700611 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800612 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700613 if (sVerbose) {
614 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
615 + ") on state " + getStateAsStringLocked());
616 }
617 return true;
618 }
Felipe Leme3103c632017-12-18 15:05:14 -0800619 if (sVerbose && isFinishedLocked()) {
620 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
621 + ") on state " + getStateAsStringLocked());
Felipe Leme17292d12017-10-24 14:03:10 -0700622 }
623 return false;
624 }
625
Felipe Lemed1146422017-04-26 10:17:05 -0700626 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700627 if (!hasAutofillFeature()) {
628 return;
629 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700630 AutofillCallback callback = null;
631 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700632 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
Felipe Lemec7b45292017-09-19 09:06:20 -0700633
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700634 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000635
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700636 if (!mEnabled) {
637 if (mCallback != null) {
638 callback = mCallback;
639 }
640 } else {
641 final AutofillId id = getAutofillId(view);
642 final AutofillValue value = view.getAutofillValue();
643
Felipe Lemec24a56a2017-08-03 14:27:57 -0700644 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700645 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700646 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700647 } else {
648 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700649 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700650 }
Felipe Leme24aae152017-03-15 12:33:01 -0700651 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800652 }
653
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700654 if (callback != null) {
655 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000656 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800657 }
658
659 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700660 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800661 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700662 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800663 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700664 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700665 if (!hasAutofillFeature()) {
666 return;
667 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700668 synchronized (mLock) {
669 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800670
Felipe Lemec24a56a2017-08-03 14:27:57 -0700671 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700672 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800673
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700674 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700675 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700676 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800677 }
678 }
679
680 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700681 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700682 *
683 * @param view {@link View} that was exited.
684 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700685 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700686 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
687 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
688 }
689
690 /**
691 * Called when a virtual view's visibility changed.
692 *
693 * @param view {@link View} that was exited.
694 * @param virtualId id identifying the virtual child inside the parent view.
695 * @param isVisible visible if the view is visible in the view hierarchy.
696 */
697 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
698 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
699 }
700
701 /**
702 * Called when a view/virtual view's visibility changed.
703 *
704 * @param view {@link View} that was exited.
705 * @param virtualId id identifying the virtual child inside the parent view.
706 * @param isVisible visible if the view is visible in the view hierarchy.
707 * @param virtual Whether the view is virtual.
708 */
709 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
710 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700711 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700712 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700713 final AutofillId id = virtual ? getAutofillId(view, virtualId)
714 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700715 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700716 if (mFillableIds.contains(id)) {
717 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
718 requestHideFillUi(id, view);
719 }
720 }
721 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700722 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700723 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700724 }
725 }
726 }
727
728 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700729 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800730 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700731 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
732 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
733 * the absolute bounds could be calculated by:
734 *
735 * <pre class="prettyprint">
736 * int offset[] = new int[2];
737 * getLocationOnScreen(offset);
738 * Rect absBounds = new Rect(bounds.left + offset[0],
739 * bounds.top + offset[1],
740 * bounds.right + offset[0], bounds.bottom + offset[1]);
741 * </pre>
742 *
743 * @param view the virtual view parent.
744 * @param virtualId id identifying the virtual child inside the parent view.
745 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800746 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700747 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
748 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700749 }
750
Felipe Leme6dcec872017-05-25 11:24:23 -0700751 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700752 if (!hasAutofillFeature()) {
753 return;
754 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700755 AutofillCallback callback = null;
756 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700757 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
758
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700759 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000760
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700761 if (!mEnabled) {
762 if (mCallback != null) {
763 callback = mCallback;
764 }
765 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700766 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700767
Felipe Lemec24a56a2017-08-03 14:27:57 -0700768 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700769 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700770 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700771 } else {
772 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700773 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700774 }
Felipe Leme24aae152017-03-15 12:33:01 -0700775 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800776 }
777
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700778 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700779 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800781 }
782 }
783
784 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700785 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800786 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700787 * @param view the virtual view parent.
788 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800789 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700790 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700791 if (!hasAutofillFeature()) {
792 return;
793 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700794 synchronized (mLock) {
795 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800796
Felipe Lemec24a56a2017-08-03 14:27:57 -0700797 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700798 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800799
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700800 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700801 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700802 }
Svet Ganov782043c2017-02-11 00:52:02 +0000803 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800804 }
805
806 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800807 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800808 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800809 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800810 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700811 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700812 if (!hasAutofillFeature()) {
813 return;
814 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700815 AutofillId id = null;
816 boolean valueWasRead = false;
817 AutofillValue value = null;
818
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700819 synchronized (mLock) {
820 // If the session is gone some fields might still be highlighted, hence we have to
821 // remove the isAutofilled property even if no sessions are active.
822 if (mLastAutofilledData == null) {
823 view.setAutofilled(false);
824 } else {
825 id = getAutofillId(view);
826 if (mLastAutofilledData.containsKey(id)) {
827 value = view.getAutofillValue();
828 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700829
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700830 if (Objects.equals(mLastAutofilledData.get(id), value)) {
831 view.setAutofilled(true);
832 } else {
833 view.setAutofilled(false);
834 mLastAutofilledData.remove(id);
835 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700836 } else {
837 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700838 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700839 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700840
Felipe Lemec24a56a2017-08-03 14:27:57 -0700841 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700842 if (sVerbose && mEnabled) {
843 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
844 + getStateAsStringLocked());
845 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700846 return;
847 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800848
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700849 if (id == null) {
850 id = getAutofillId(view);
851 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700852
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700853 if (!valueWasRead) {
854 value = view.getAutofillValue();
855 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700856
Felipe Leme0aa4c502017-04-26 12:36:01 -0700857 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700858 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800859 }
860
Felipe Lemebab851c2017-02-03 18:45:08 -0800861 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700862 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800863 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700864 * @param view the virtual view parent.
865 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800866 * @param value new value of the child.
867 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700868 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700869 if (!hasAutofillFeature()) {
870 return;
871 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700872 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700873 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700874 return;
875 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800876
Felipe Leme6dcec872017-05-25 11:24:23 -0700877 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700878 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700879 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800880 }
881
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700882
883 /**
884 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
885 *
886 * @hide
887 */
888 public void notifyViewClicked(View view) {
889 final AutofillId id = view.getAutofillId();
890
891 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
892
893 synchronized (mLock) {
894 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
895 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
896 commitLocked();
897 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
898 mContext.getPackageName());
899 }
900 }
901 }
902
903 /**
904 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
905 *
906 * @hide
907 */
908 public void onActivityFinished() {
909 if (!hasAutofillFeature()) {
910 return;
911 }
912 synchronized (mLock) {
913 if (mSaveOnFinish) {
Felipe Leme601d2202017-11-17 08:21:23 -0800914 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700915 commitLocked();
916 } else {
917 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
918 cancelLocked();
919 }
920 }
921 }
922
Felipe Lemebab851c2017-02-03 18:45:08 -0800923 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700924 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800925 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700926 * <p>This method is typically called by {@link View Views} that manage virtual views; for
927 * example, when the view is rendering an {@code HTML} page with a form and virtual views
928 * that represent the HTML elements, it should call this method after the form is submitted and
929 * another page is rendered.
930 *
931 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
932 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800933 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700934 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700935 if (!hasAutofillFeature()) {
936 return;
937 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700938 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700939 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700940 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800941 }
942
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700943 private void commitLocked() {
944 if (!mEnabled && !isActiveLocked()) {
945 return;
946 }
947 finishSessionLocked();
948 }
949
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700950 /**
951 * Called to indicate the current autofill context should be cancelled.
952 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700953 * <p>This method is typically called by {@link View Views} that manage virtual views; for
954 * example, when the view is rendering an {@code HTML} page with a form and virtual views
955 * that represent the HTML elements, it should call this method if the user does not post the
956 * form but moves to another form in this page.
957 *
958 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
959 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700960 */
961 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -0800962 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -0700963 if (!hasAutofillFeature()) {
964 return;
965 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700966 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700967 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700968 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700969 }
970
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700971 private void cancelLocked() {
972 if (!mEnabled && !isActiveLocked()) {
973 return;
974 }
975 cancelSessionLocked();
976 }
977
Svet Ganovf965b872017-04-28 16:34:02 -0700978 /** @hide */
979 public void disableOwnedAutofillServices() {
980 disableAutofillServices();
981 }
982
Svet Ganovf20a0372017-04-10 17:08:05 -0700983 /**
984 * If the app calling this API has enabled autofill services they
985 * will be disabled.
986 */
Svet Ganovf965b872017-04-28 16:34:02 -0700987 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700988 if (!hasAutofillFeature()) {
989 return;
990 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700991 try {
992 mService.disableOwnedAutofillServices(mContext.getUserId());
993 } catch (RemoteException e) {
994 throw e.rethrowFromSystemServer();
995 }
996 }
997
Felipe Lemedb041182017-04-21 17:33:38 -0700998 /**
999 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1000 * enabled for the current user, or {@code false} otherwise.
1001 */
1002 public boolean hasEnabledAutofillServices() {
1003 if (mService == null) return false;
1004
1005 try {
1006 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1007 } catch (RemoteException e) {
1008 throw e.rethrowFromSystemServer();
1009 }
1010 }
1011
1012 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001013 * Returns the component name of the {@link AutofillService} that is enabled for the current
1014 * user.
1015 */
1016 @Nullable
1017 public ComponentName getAutofillServiceComponentName() {
1018 if (mService == null) return null;
1019
1020 try {
1021 return mService.getAutofillServiceComponentName();
1022 } catch (RemoteException e) {
1023 throw e.rethrowFromSystemServer();
1024 }
1025 }
1026
1027 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001028 * Gets the user data used for
1029 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001030 *
Felipe Leme27f45732017-12-22 09:05:22 -08001031 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1032 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1033 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001034 *
Felipe Leme452886a2017-11-27 13:09:13 -08001035 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1036 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001037 */
Felipe Leme452886a2017-11-27 13:09:13 -08001038 @Nullable public UserData getUserData() {
1039 try {
1040 return mService.getUserData();
1041 } catch (RemoteException e) {
1042 e.rethrowFromSystemServer();
1043 return null;
1044 }
1045 }
1046
1047 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001048 * Sets the user data used for
1049 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001050 *
1051 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1052 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1053 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001054 */
Felipe Leme452886a2017-11-27 13:09:13 -08001055 public void setUserData(@Nullable UserData userData) {
1056 try {
1057 mService.setUserData(userData);
1058 } catch (RemoteException e) {
1059 e.rethrowFromSystemServer();
1060 }
1061 }
1062
1063 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001064 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1065 * enabled.
1066 *
1067 * <p>As field classification is an expensive operation, it could be disabled, either
1068 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1069 * permanently (for example, because the device is a low-level device).
1070 *
1071 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1072 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1073 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001074 */
Felipe Leme329d0402017-12-06 09:22:43 -08001075 public boolean isFieldClassificationEnabled() {
1076 try {
1077 return mService.isFieldClassificationEnabled();
1078 } catch (RemoteException e) {
1079 e.rethrowFromSystemServer();
1080 return false;
1081 }
1082 }
1083
1084 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001085 * Gets the name of the default algorithm used for
1086 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1087 *
1088 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1089 * set.
1090 *
1091 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1092 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1093 * the user.
1094 */
1095 @Nullable
1096 public String getDefaultFieldClassificationAlgorithm() {
1097 try {
1098 return mService.getDefaultFieldClassificationAlgorithm();
1099 } catch (RemoteException e) {
1100 e.rethrowFromSystemServer();
1101 return null;
1102 }
1103 }
1104
1105 /**
1106 * Gets the name of all algorithms currently available for
1107 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1108 *
1109 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1110 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1111 * the user.
1112 *
1113 * @return list of all algorithms currently available, or an empty list if the caller currently
1114 * does not have an enabled autofill service for the user.
1115 */
1116 @NonNull
1117 public List<String> getAvailableFieldClassificationAlgorithms() {
1118 try {
1119 final List<String> names = mService.getAvailableFieldClassificationAlgorithms();
1120 return names != null ? names : Collections.emptyList();
1121 } catch (RemoteException e) {
1122 e.rethrowFromSystemServer();
1123 return null;
1124 }
1125 }
1126
1127 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001128 * Returns {@code true} if autofill is supported by the current device and
1129 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001130 *
1131 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1132 * <ol>
1133 * <li>Low-end devices.
1134 * <li>Device policy rules that forbid its usage.
1135 * </ol>
1136 */
1137 public boolean isAutofillSupported() {
1138 if (mService == null) return false;
1139
1140 try {
1141 return mService.isServiceSupported(mContext.getUserId());
1142 } catch (RemoteException e) {
1143 throw e.rethrowFromSystemServer();
1144 }
1145 }
1146
Felipe Leme637e05e2017-12-06 12:09:37 -08001147 // Note: don't need to use locked suffix because mContext is final.
1148 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001149 final AutofillClient client = mContext.getAutofillClient();
1150 if (client == null && sDebug) {
1151 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1152 + mContext);
1153 }
1154 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001155 }
1156
1157 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001158 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -07001159 if (!hasAutofillFeature()) {
1160 return;
1161 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001162 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001163 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1164 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1165 // service set the extra and returned RESULT_CANCELED...
1166
Felipe Leme9f9ee252017-04-27 13:56:22 -07001167 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001168
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001169 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001170 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001171 return;
1172 }
1173 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1174 final Bundle responseData = new Bundle();
1175 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001176 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1177 if (newClientState != null) {
1178 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1179 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001180 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001181 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1182 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001183 } catch (RemoteException e) {
1184 Log.e(TAG, "Error delivering authentication result", e);
1185 }
Svet Ganov782043c2017-02-11 00:52:02 +00001186 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001187 }
1188
Felipe Leme640f30a2017-03-06 15:44:06 -08001189 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001190 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001191 }
1192
Felipe Leme6dcec872017-05-25 11:24:23 -07001193 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001194 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001195 }
1196
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001197 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1198 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001199 if (sVerbose) {
1200 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -07001201 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001202 }
Felipe Leme3103c632017-12-18 15:05:14 -08001203 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001204 if (sVerbose) {
1205 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001206 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001207 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001208 return;
1209 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001210 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001211 final AutofillClient client = getClient();
1212 if (client == null) return; // NOTE: getClient() already logd it..
1213
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001214 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001215 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001216 mCallback != null, flags, client.getComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001217 if (mSessionId != NO_SESSION) {
1218 mState = STATE_ACTIVE;
1219 }
Felipe Leme637e05e2017-12-06 12:09:37 -08001220 client.autofillCallbackResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001221 } catch (RemoteException e) {
1222 throw e.rethrowFromSystemServer();
1223 }
1224 }
1225
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001226 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001227 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001228
1229 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001230
Svet Ganov782043c2017-02-11 00:52:02 +00001231 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001232 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001233 } catch (RemoteException e) {
1234 throw e.rethrowFromSystemServer();
1235 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001236
Felipe Lemec24a56a2017-08-03 14:27:57 -07001237 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001238 }
1239
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001240 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001241 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001242
1243 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001244
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001245 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001246 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001247 } catch (RemoteException e) {
1248 throw e.rethrowFromSystemServer();
1249 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001250
Svet Ganov48f10a22017-04-26 18:49:30 -07001251 resetSessionLocked();
1252 }
1253
1254 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001255 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001256 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001257 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001258 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001259 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001260 }
1261
Felipe Leme0aa4c502017-04-26 12:36:01 -07001262 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1263 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001264 if (sVerbose && action != ACTION_VIEW_EXITED) {
1265 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1266 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001267 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001268 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1269
Felipe Leme3461d3c2017-01-19 08:54:55 -08001270 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001271 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001272 final AutofillClient client = getClient();
1273 if (client == null) return; // NOTE: getClient() already logd it..
1274
Felipe Leme7f33cd32017-05-11 10:10:49 -07001275 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1276 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001277 mCallback != null, flags, client.getComponentName(), mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001278 if (newId != mSessionId) {
1279 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1280 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001281 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme637e05e2017-12-06 12:09:37 -08001282 client.autofillCallbackResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001283 }
1284 } else {
1285 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1286 mContext.getUserId());
1287 }
1288
Felipe Leme3461d3c2017-01-19 08:54:55 -08001289 } catch (RemoteException e) {
1290 throw e.rethrowFromSystemServer();
1291 }
1292 }
Svet Ganov782043c2017-02-11 00:52:02 +00001293
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001294 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001295 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001296 return;
1297 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001298
Svet Ganov782043c2017-02-11 00:52:02 +00001299 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001300 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001301 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001302 final int userId = mContext.getUserId();
1303 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001304 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1305 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1306 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001307 final IAutoFillManager service = mService;
1308 final IAutoFillManagerClient serviceClient = mServiceClient;
1309 mServiceClientCleaner = Cleaner.create(this, () -> {
1310 try {
1311 service.removeClient(serviceClient, userId);
1312 } catch (RemoteException e) {
1313 }
1314 });
Svet Ganov782043c2017-02-11 00:52:02 +00001315 } catch (RemoteException e) {
1316 throw e.rethrowFromSystemServer();
1317 }
1318 }
1319 }
1320
Felipe Lemee6010f22017-03-03 11:19:51 -08001321 /**
1322 * Registers a {@link AutofillCallback} to receive autofill events.
1323 *
1324 * @param callback callback to receive events.
1325 */
1326 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001327 if (!hasAutofillFeature()) {
1328 return;
1329 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001330 synchronized (mLock) {
1331 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001332
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001333 final boolean hadCallback = mCallback != null;
1334 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001335
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001336 if (!hadCallback) {
1337 try {
1338 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1339 } catch (RemoteException e) {
1340 throw e.rethrowFromSystemServer();
1341 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001342 }
1343 }
1344 }
1345
1346 /**
1347 * Unregisters a {@link AutofillCallback} to receive autofill events.
1348 *
1349 * @param callback callback to stop receiving events.
1350 */
1351 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001352 if (!hasAutofillFeature()) {
1353 return;
1354 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001355 synchronized (mLock) {
1356 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001357
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001358 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001359
Felipe Lemee6010f22017-03-03 11:19:51 -08001360 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001361 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001362 } catch (RemoteException e) {
1363 throw e.rethrowFromSystemServer();
1364 }
1365 }
1366 }
1367
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001368 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1369 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1370 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001371 if (anchor == null) {
1372 return;
1373 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001374
1375 AutofillCallback callback = null;
1376 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001377 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001378 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001379
1380 if (client != null) {
1381 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1382 anchorBounds, presenter) && mCallback != null) {
1383 callback = mCallback;
1384 }
1385 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001386 }
1387 }
1388
1389 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001390 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001391 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001392 AutofillCallback.EVENT_INPUT_SHOWN);
1393 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001394 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001395 }
1396 }
1397 }
1398
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001399 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1400 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001401 synchronized (mLock) {
1402 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001403 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001404 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001405 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001406 }
1407 }
1408 }
1409 }
1410
Felipe Leme51e29da2017-10-24 14:03:10 -07001411 /** @hide */
1412 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1413 /** @hide */
1414 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1415 /** @hide */
1416 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1417 /** @hide */
1418 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1419 /** @hide */
1420 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1421
1422 private void setState(int flags) {
1423 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001424 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001425 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1426 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001427 // Reset the session state
1428 resetSessionLocked();
1429 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001430 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001431 // Reset connection to system
1432 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001433 if (mServiceClientCleaner != null) {
1434 mServiceClientCleaner.clean();
1435 mServiceClientCleaner = null;
1436 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001437 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001438 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001439 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1440 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001441 }
1442
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001443 /**
1444 * Sets a view as autofilled if the current value is the {code targetValue}.
1445 *
1446 * @param view The view that is to be autofilled
1447 * @param targetValue The value we want to fill into view
1448 */
1449 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1450 AutofillValue currentValue = view.getAutofillValue();
1451 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001452 synchronized (mLock) {
1453 if (mLastAutofilledData == null) {
1454 mLastAutofilledData = new ParcelableMap(1);
1455 }
1456 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001457 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001458 view.setAutofilled(true);
1459 }
1460 }
1461
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001462 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001463 synchronized (mLock) {
1464 if (sessionId != mSessionId) {
1465 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001466 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001467
Felipe Leme637e05e2017-12-06 12:09:37 -08001468 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001469 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001470 return;
1471 }
1472
1473 final int itemCount = ids.size();
1474 int numApplied = 0;
1475 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001476 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001477
1478 for (int i = 0; i < itemCount; i++) {
1479 final AutofillId id = ids.get(i);
1480 final AutofillValue value = values.get(i);
1481 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001482 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001483 if (view == null) {
1484 Log.w(TAG, "autofill(): no View with id " + viewId);
1485 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001486 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001487 if (id.isVirtual()) {
1488 if (virtualValues == null) {
1489 // Most likely there will be just one view with virtual children.
1490 virtualValues = new ArrayMap<>(1);
1491 }
1492 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1493 if (valuesByParent == null) {
1494 // We don't know the size yet, but usually it will be just a few fields...
1495 valuesByParent = new SparseArray<>(5);
1496 virtualValues.put(view, valuesByParent);
1497 }
1498 valuesByParent.put(id.getVirtualChildId(), value);
1499 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001500 // Mark the view as to be autofilled with 'value'
1501 if (mLastAutofilledData == null) {
1502 mLastAutofilledData = new ParcelableMap(itemCount - i);
1503 }
1504 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001505
1506 view.autofill(value);
1507
1508 // Set as autofilled if the values match now, e.g. when the value was updated
1509 // synchronously.
1510 // If autofill happens async, the view is set to autofilled in
1511 // notifyValueChanged.
1512 setAutofilledIfValuesIs(view, value);
1513
1514 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001515 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001516 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001517
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001518 if (virtualValues != null) {
1519 for (int i = 0; i < virtualValues.size(); i++) {
1520 final View parent = virtualValues.keyAt(i);
1521 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1522 parent.autofill(childrenValues);
1523 numApplied += childrenValues.size();
1524 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001525 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001526
Felipe Lemeb22d6352017-09-08 20:03:53 -07001527 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1528 .setPackageName(mContext.getPackageName())
1529 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1530 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001531 mMetricsLogger.write(log);
1532 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001533 }
1534
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001535 /**
1536 * Set the tracked views.
1537 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001538 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001539 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001540 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001541 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001542 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001543 */
Felipe Leme27e20222017-05-18 15:24:11 -07001544 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001545 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1546 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001547 synchronized (mLock) {
1548 if (mEnabled && mSessionId == sessionId) {
1549 if (saveOnAllViewsInvisible) {
1550 mTrackedViews = new TrackedViews(trackedIds);
1551 } else {
1552 mTrackedViews = null;
1553 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001554 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001555 if (fillableIds != null) {
1556 if (mFillableIds == null) {
1557 mFillableIds = new ArraySet<>(fillableIds.length);
1558 }
1559 for (AutofillId id : fillableIds) {
1560 mFillableIds.add(id);
1561 }
1562 if (sVerbose) {
1563 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1564 + ", mFillableIds" + mFillableIds);
1565 }
1566 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001567
1568 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1569 // Turn off trigger on previous view id.
1570 setNotifyOnClickLocked(mSaveTriggerId, false);
1571 }
1572
1573 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1574 // Turn on trigger on new view id.
1575 mSaveTriggerId = saveTriggerId;
1576 setNotifyOnClickLocked(mSaveTriggerId, true);
1577 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001578 }
1579 }
1580 }
1581
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001582 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1583 final View view = findView(id);
1584 if (view == null) {
1585 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1586 return;
1587 }
1588 view.setNotifyAutofillManagerOnClick(notify);
1589 }
1590
Felipe Lemec24a56a2017-08-03 14:27:57 -07001591 private void setSaveUiState(int sessionId, boolean shown) {
1592 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1593 synchronized (mLock) {
1594 if (mSessionId != NO_SESSION) {
1595 // Race condition: app triggered a new session after the previous session was
1596 // finished but before server called setSaveUiState() - need to cancel the new
1597 // session to avoid further inconsistent behavior.
1598 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1599 + ") called on existing session " + mSessionId + "; cancelling it");
1600 cancelSessionLocked();
1601 }
1602 if (shown) {
1603 mSessionId = sessionId;
1604 mState = STATE_SHOWING_SAVE_UI;
1605 } else {
1606 mSessionId = NO_SESSION;
1607 mState = STATE_UNKNOWN;
1608 }
1609 }
1610 }
1611
Felipe Leme650f7ab2017-09-19 13:08:24 -07001612 /**
1613 * Marks the state of the session as finished.
1614 *
1615 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001616 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1617 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1618 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001619 */
1620 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001621 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001622 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001623 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001624 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001625 }
1626 }
1627
Felipe Leme27e20222017-05-18 15:24:11 -07001628 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001629 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001630 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001631 if (anchor == null) {
1632 return;
1633 }
Felipe Leme27e20222017-05-18 15:24:11 -07001634 requestHideFillUi(id, anchor);
1635 }
1636
1637 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001638
1639 AutofillCallback callback = null;
1640 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001641 // We do not check the session id for two reasons:
1642 // 1. If local and remote session id are off sync the UI would be stuck shown
1643 // 2. There is a race between the user state being destroyed due the fill
1644 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001645 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001646 if (client != null) {
1647 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1648 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001649 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001650 }
1651 }
1652
1653 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001654 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001655 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001656 AutofillCallback.EVENT_INPUT_HIDDEN);
1657 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001658 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001659 }
1660 }
1661 }
1662
Felipe Leme17292d12017-10-24 14:03:10 -07001663 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001664 if (sVerbose) {
1665 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001666 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001667 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001668 final View anchor = findView(id);
1669 if (anchor == null) {
1670 return;
1671 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001672
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001673 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001674 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001675 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001676 callback = mCallback;
1677 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001678 }
1679
1680 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001681 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001682 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001683 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1684 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001685 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001686 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001687 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001688
Felipe Leme17292d12017-10-24 14:03:10 -07001689 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001690 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001691 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001692 }
1693 }
1694
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001695 /**
1696 * Get an array of viewIds from a List of {@link AutofillId}.
1697 *
1698 * @param autofillIds The autofill ids to convert
1699 *
1700 * @return The array of viewIds.
1701 */
Felipe Leme27e20222017-05-18 15:24:11 -07001702 // TODO: move to Helper as static method
1703 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1704 final int numIds = autofillIds.length;
1705 final int[] viewIds = new int[numIds];
1706 for (int i = 0; i < numIds; i++) {
1707 viewIds[i] = autofillIds[i].getViewId();
1708 }
1709
1710 return viewIds;
1711 }
1712
1713 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001714 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1715 final int numIds = autofillIds.size();
1716 final int[] viewIds = new int[numIds];
1717 for (int i = 0; i < numIds; i++) {
1718 viewIds[i] = autofillIds.get(i).getViewId();
1719 }
1720
1721 return viewIds;
1722 }
1723
1724 /**
1725 * Find a single view by its id.
1726 *
1727 * @param autofillId The autofill id of the view
1728 *
1729 * @return The view or {@code null} if view was not found
1730 */
1731 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001732 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001733
1734 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001735 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001736 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001737
Phil Weaver846cda932017-06-15 10:10:06 -07001738 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001739 }
1740
Felipe Lemebb810922017-04-25 15:54:06 -07001741 /** @hide */
1742 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001743 return mService != null;
1744 }
1745
Felipe Lemec24a56a2017-08-03 14:27:57 -07001746 /** @hide */
1747 public void onPendingSaveUi(int operation, IBinder token) {
1748 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1749
1750 synchronized (mLock) {
1751 try {
1752 mService.onPendingSaveUi(operation, token);
1753 } catch (RemoteException e) {
1754 e.rethrowFromSystemServer();
1755 }
1756 }
1757 }
1758
1759 /** @hide */
1760 public void dump(String outerPrefix, PrintWriter pw) {
1761 pw.print(outerPrefix); pw.println("AutofillManager:");
1762 final String pfx = outerPrefix + " ";
1763 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001764 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001765 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001766 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001767 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1768 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1769 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1770 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1771 pw.print(pfx); pw.print("tracked views: ");
1772 if (mTrackedViews == null) {
1773 pw.println("null");
1774 } else {
1775 final String pfx2 = pfx + " ";
1776 pw.println();
1777 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1778 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1779 }
1780 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001781 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1782 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme51e29da2017-10-24 14:03:10 -07001783 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1784 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001785 }
1786
Felipe Lemec7b45292017-09-19 09:06:20 -07001787 private String getStateAsStringLocked() {
1788 switch (mState) {
1789 case STATE_UNKNOWN:
1790 return "STATE_UNKNOWN";
1791 case STATE_ACTIVE:
1792 return "STATE_ACTIVE";
1793 case STATE_FINISHED:
1794 return "STATE_FINISHED";
1795 case STATE_SHOWING_SAVE_UI:
1796 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001797 case STATE_DISABLED_BY_SERVICE:
1798 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001799 default:
1800 return "INVALID:" + mState;
1801 }
1802 }
1803
Felipe Lemec24a56a2017-08-03 14:27:57 -07001804 private boolean isActiveLocked() {
1805 return mState == STATE_ACTIVE;
1806 }
1807
Felipe Leme3103c632017-12-18 15:05:14 -08001808 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07001809 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001810 }
1811
Felipe Leme3103c632017-12-18 15:05:14 -08001812 private boolean isFinishedLocked() {
1813 return mState == STATE_FINISHED;
1814 }
1815
Felipe Leme9876a6f2017-05-30 15:47:28 -07001816 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001817 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07001818 if (client == null) {
1819 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1820 return;
1821 }
1822 client.runOnUiThread(runnable);
1823 }
1824
Felipe Lemee6010f22017-03-03 11:19:51 -08001825 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001826 * View tracking information. Once all tracked views become invisible the session is finished.
1827 */
1828 private class TrackedViews {
1829 /** Visible tracked views */
1830 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1831
1832 /** Invisible tracked views */
1833 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1834
1835 /**
1836 * Check if set is null or value is in set.
1837 *
1838 * @param set The set or null (== empty set)
1839 * @param value The value that might be in the set
1840 *
1841 * @return {@code true} iff set is not empty and value is in set
1842 */
Felipe Leme27e20222017-05-18 15:24:11 -07001843 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001844 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1845 return set != null && set.contains(value);
1846 }
1847
1848 /**
1849 * Add a value to a set. If set is null, create a new set.
1850 *
1851 * @param set The set or null (== empty set)
1852 * @param valueToAdd The value to add
1853 *
1854 * @return The set including the new value. If set was {@code null}, a set containing only
1855 * the new value.
1856 */
Felipe Leme27e20222017-05-18 15:24:11 -07001857 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001858 @NonNull
1859 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1860 if (set == null) {
1861 set = new ArraySet<>(1);
1862 }
1863
1864 set.add(valueToAdd);
1865
1866 return set;
1867 }
1868
1869 /**
1870 * Remove a value from a set.
1871 *
1872 * @param set The set or null (== empty set)
1873 * @param valueToRemove The value to remove
1874 *
1875 * @return The set without the removed value. {@code null} if set was null, or is empty
1876 * after removal.
1877 */
Felipe Leme27e20222017-05-18 15:24:11 -07001878 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001879 @Nullable
1880 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1881 if (set == null) {
1882 return null;
1883 }
1884
1885 set.remove(valueToRemove);
1886
1887 if (set.isEmpty()) {
1888 return null;
1889 }
1890
1891 return set;
1892 }
1893
1894 /**
1895 * Set the tracked views.
1896 *
1897 * @param trackedIds The views to be tracked
1898 */
Felipe Leme27e20222017-05-18 15:24:11 -07001899 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001900 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001901 if (trackedIds != null && client != null) {
1902 final boolean[] isVisible;
1903
1904 if (client.isVisibleForAutofill()) {
1905 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1906 } else {
1907 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001908 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001909 }
1910
Felipe Leme27e20222017-05-18 15:24:11 -07001911 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001912 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001913 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001914
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001915 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001916 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001917 } else {
1918 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1919 }
1920 }
1921 }
1922
Felipe Leme9f9ee252017-04-27 13:56:22 -07001923 if (sVerbose) {
1924 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001925 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1926 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1927 }
1928
1929 if (mVisibleTrackedIds == null) {
1930 finishSessionLocked();
1931 }
1932 }
1933
1934 /**
1935 * Called when a {@link View view's} visibility changes.
1936 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001937 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001938 * @param isVisible visible if the view is visible in the view hierarchy.
1939 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001940 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001941 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001942
Felipe Leme9f9ee252017-04-27 13:56:22 -07001943 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001944 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001945 + isVisible);
1946 }
1947
1948 if (client != null && client.isVisibleForAutofill()) {
1949 if (isVisible) {
1950 if (isInSet(mInvisibleTrackedIds, id)) {
1951 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1952 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1953 }
1954 } else {
1955 if (isInSet(mVisibleTrackedIds, id)) {
1956 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1957 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1958 }
1959 }
1960 }
1961
1962 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001963 if (sVerbose) {
1964 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1965 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001966 finishSessionLocked();
1967 }
1968 }
1969
1970 /**
1971 * Called once the client becomes visible.
1972 *
1973 * @see AutofillClient#isVisibleForAutofill()
1974 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001975 void onVisibleForAutofillLocked() {
1976 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001977 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08001978 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001979 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1980 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1981 if (client != null) {
1982 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001983 final ArrayList<AutofillId> orderedInvisibleIds =
1984 new ArrayList<>(mInvisibleTrackedIds);
1985 final boolean[] isVisible = client.getViewVisibility(
1986 getViewIds(orderedInvisibleIds));
1987
1988 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1989 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1990 final AutofillId id = orderedInvisibleIds.get(i);
1991 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001992 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1993
Felipe Leme9f9ee252017-04-27 13:56:22 -07001994 if (sDebug) {
1995 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001996 }
1997 } else {
1998 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1999 }
2000 }
2001 }
2002
2003 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002004 final ArrayList<AutofillId> orderedVisibleIds =
2005 new ArrayList<>(mVisibleTrackedIds);
2006 final boolean[] isVisible = client.getViewVisibility(
2007 getViewIds(orderedVisibleIds));
2008
2009 final int numVisibleTrackedIds = orderedVisibleIds.size();
2010 for (int i = 0; i < numVisibleTrackedIds; i++) {
2011 final AutofillId id = orderedVisibleIds.get(i);
2012
2013 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002014 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2015 } else {
2016 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2017
Felipe Leme9f9ee252017-04-27 13:56:22 -07002018 if (sDebug) {
2019 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002020 }
2021 }
2022 }
2023 }
2024
2025 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2026 mVisibleTrackedIds = updatedVisibleTrackedIds;
2027 }
2028
2029 if (mVisibleTrackedIds == null) {
2030 finishSessionLocked();
2031 }
2032 }
2033 }
2034
2035 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002036 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002037 *
2038 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002039 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002040 */
2041 public abstract static class AutofillCallback {
2042
2043 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002044 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2045 EVENT_INPUT_SHOWN,
2046 EVENT_INPUT_HIDDEN,
2047 EVENT_INPUT_UNAVAILABLE
2048 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002049 @Retention(RetentionPolicy.SOURCE)
2050 public @interface AutofillEventType {}
2051
2052 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002053 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002054 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002055 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002056 * should be hidden upon receiving this event.
2057 */
2058 public static final int EVENT_INPUT_SHOWN = 1;
2059
2060 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002061 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002062 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002063 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002064 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2065 */
2066 public static final int EVENT_INPUT_HIDDEN = 2;
2067
2068 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002069 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002070 * autofill is not available.
2071 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002072 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002073 * to avoid flickering, it could shown it upon receiving this event.
2074 */
2075 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2076
2077 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002078 * Called after a change in the autofill state associated with a view.
2079 *
2080 * @param view view associated with the change.
2081 *
2082 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2083 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002084 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2085 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002086
2087 /**
2088 * Called after a change in the autofill state associated with a virtual view.
2089 *
2090 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002091 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002092 *
2093 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2094 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002095 public void onAutofillEvent(@NonNull View view, int virtualId,
2096 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002097 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002098 }
2099
Felipe Leme640f30a2017-03-06 15:44:06 -08002100 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2101 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002102
Felipe Leme640f30a2017-03-06 15:44:06 -08002103 AutofillManagerClient(AutofillManager autofillManager) {
2104 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002105 }
2106
2107 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002108 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002109 final AutofillManager afm = mAfm.get();
2110 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002111 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002112 }
2113 }
2114
2115 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002116 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002117 final AutofillManager afm = mAfm.get();
2118 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002119 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002120 }
2121 }
2122
2123 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002124 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2125 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002126 final AutofillManager afm = mAfm.get();
2127 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002128 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002129 }
2130 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002131
2132 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002133 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2134 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002135 final AutofillManager afm = mAfm.get();
2136 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002137 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2138 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002139 }
2140 }
2141
2142 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002143 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002144 final AutofillManager afm = mAfm.get();
2145 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002146 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002147 }
2148 }
2149
2150 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002151 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002152 final AutofillManager afm = mAfm.get();
2153 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002154 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002155 }
2156 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002157
2158 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002159 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002160 final AutofillManager afm = mAfm.get();
2161 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002162 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002163 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002164 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002165 } catch (IntentSender.SendIntentException e) {
2166 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2167 }
2168 });
2169 }
2170 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002171
2172 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002173 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002174 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2175 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002176 final AutofillManager afm = mAfm.get();
2177 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002178 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2179 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002180 }
2181 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002182
2183 @Override
2184 public void setSaveUiState(int sessionId, boolean shown) {
2185 final AutofillManager afm = mAfm.get();
2186 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002187 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2188 }
2189 }
2190
2191 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002192 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002193 final AutofillManager afm = mAfm.get();
2194 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002195 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002196 }
2197 }
Svet Ganov782043c2017-02-11 00:52:02 +00002198 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002199}