blob: 9241ec0046acc6baf321cb632b69e8c33fdb2832 [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 Leme4753bb02017-03-22 20:24:00 -070039import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070040import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080041import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070042import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080043import android.view.View;
44
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070045import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070046import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070047import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme4753bb02017-03-22 20:24:00 -070048
Felipe Lemec24a56a2017-08-03 14:27:57 -070049import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000052import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070053import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000054import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070055import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000056
Koji Fukuiccec6a62017-10-18 17:48:40 +090057// TODO: use java.lang.ref.Cleaner once Android supports Java 9
58import sun.misc.Cleaner;
59
Felipe Leme3461d3c2017-01-19 08:54:55 -080060/**
Felipe Leme744976e2017-07-31 11:34:14 -070061 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
62 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070063 *
Felipe Leme744976e2017-07-31 11:34:14 -070064 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
65 * activity context; the autofill context is created when one of the following methods is called for
66 * the first time in an activity context, and the current user has an enabled autofill service:
67 *
68 * <ul>
69 * <li>{@link #notifyViewEntered(View)}
70 * <li>{@link #notifyViewEntered(View, int, Rect)}
71 * <li>{@link #requestAutofill(View)}
72 * </ul>
73 *
74 * <p>Tipically, the context is automatically created when the first view of the activity is
75 * focused because {@code View.onFocusChanged()} indirectly calls
76 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
77 * explicitly create it (for example, a custom view developer could offer a contextual menu action
78 * in a text-field view to let users manually request autofill).
79 *
80 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
81 * that represents the view hierarchy by calling
82 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
83 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
84 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
85 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
86 * the hierarchy.
87 *
88 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
89 * parses it looking for views that can be autofilled. If the service finds such views, it returns
90 * a data structure to the Android System containing the following optional info:
91 *
92 * <ul>
93 * <li>Datasets used to autofill subsets of views in the activity.
94 * <li>Id of views that the service can save their values for future autofilling.
95 * </ul>
96 *
97 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -070098 * UI associated with the view, when the view is focused on and is part of a dataset.
99 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700100 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700101 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700102 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
103 *
104 * <p>When the service returns ids of savable views, the Android System keeps track of changes
105 * made to these views, so they can be used to determine if the autofill save UI is shown later.
106 *
107 * <p>The context is then finished when one of the following occurs:
108 *
109 * <ul>
110 * <li>{@link #commit()} is called or all savable views are gone.
111 * <li>{@link #cancel()} is called.
112 * </ul>
113 *
114 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700115 * 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 -0700116 * option to Save, the current value of the views is then sent to the autofill service.
117 *
118 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800119 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600120@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800121public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800122
Felipe Leme640f30a2017-03-06 15:44:06 -0800123 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800124
Svet Ganov782043c2017-02-11 00:52:02 +0000125 /**
126 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700127 *
Svet Ganov782043c2017-02-11 00:52:02 +0000128 * <p>
129 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000130 */
131 public static final String EXTRA_ASSIST_STRUCTURE =
132 "android.view.autofill.extra.ASSIST_STRUCTURE";
133
134 /**
135 * Intent extra: The result of an authentication operation. It is
136 * either a fully populated {@link android.service.autofill.FillResponse}
137 * or a fully populated {@link android.service.autofill.Dataset} if
138 * a response or a dataset is being authenticated respectively.
139 *
140 * <p>
141 * Type: {@link android.service.autofill.FillResponse} or a
142 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000143 */
144 public static final String EXTRA_AUTHENTICATION_RESULT =
145 "android.view.autofill.extra.AUTHENTICATION_RESULT";
146
Felipe Leme7320ca92017-03-29 15:09:54 -0700147 /**
148 * Intent extra: The optional extras provided by the
149 * {@link android.service.autofill.AutofillService}.
150 *
151 * <p>For example, when the service responds to a {@link
152 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
153 * a {@code FillResponse} that requires authentication, the Intent that launches the
154 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700155 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700156 *
Felipe Lemea9372382017-10-09 14:42:36 -0700157 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
158 * can also add this bundle to the {@link Intent} set as the
159 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
160 * so the bundle can be recovered later on
161 * {@link android.service.autofill.SaveRequest#getClientState()}.
162 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700163 * <p>
164 * Type: {@link android.os.Bundle}
165 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700166 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600167 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700168
Felipe Lemec24a56a2017-08-03 14:27:57 -0700169
170 /** @hide */
171 public static final String EXTRA_RESTORE_SESSION_TOKEN =
172 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
173
174 private static final String SESSION_ID_TAG = "android:sessionId";
175 private static final String STATE_TAG = "android:state";
176 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
177
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700178
Felipe Leme0aa4c502017-04-26 12:36:01 -0700179 /** @hide */ public static final int ACTION_START_SESSION = 1;
180 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
181 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
182 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800183
Felipe Leme9f9ee252017-04-27 13:56:22 -0700184
185 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
186 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
187 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
188
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700189 /** Which bits in an authentication id are used for the dataset id */
190 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
191 /** How many bits in an authentication id are used for the dataset id */
192 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
193 /** @hide The index for an undefined data set */
194 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
195
196 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700197 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
198 *
199 * @hide
200 */
201 public static final int PENDING_UI_OPERATION_CANCEL = 1;
202
203 /**
204 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
205 *
206 * @hide
207 */
208 public static final int PENDING_UI_OPERATION_RESTORE = 2;
209
210 /**
211 * Initial state of the autofill context, set when there is no session (i.e., when
212 * {@link #mSessionId} is {@link #NO_SESSION}).
213 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700214 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
215 * the server.
216 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700217 * @hide
218 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700219 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700220
221 /**
222 * State where the autofill context hasn't been {@link #commit() finished} nor
223 * {@link #cancel() canceled} yet.
224 *
225 * @hide
226 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700227 public static final int STATE_ACTIVE = 1;
228
229 /**
230 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700231 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700232 *
233 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
234 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
235 *
236 * @hide
237 */
238 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700239
240 /**
241 * State where the autofill context has been {@link #commit() finished} but the server still has
242 * a session because the Save UI hasn't been dismissed yet.
243 *
244 * @hide
245 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700246 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700247
248 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700249 * State where the autofill is disabled because the service cannot autofill the activity at all.
250 *
251 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
252 * (and {@link #requestAutofill(View, int, Rect)}).
253 *
254 * @hide
255 */
256 public static final int STATE_DISABLED_BY_SERVICE = 4;
257
258 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700259 * Makes an authentication id from a request id and a dataset id.
260 *
261 * @param requestId The request id.
262 * @param datasetId The dataset id.
263 * @return The authentication id.
264 * @hide
265 */
266 public static int makeAuthenticationId(int requestId, int datasetId) {
267 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
268 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
269 }
270
271 /**
272 * Gets the request id from an authentication id.
273 *
274 * @param authRequestId The authentication id.
275 * @return The request id.
276 * @hide
277 */
278 public static int getRequestIdFromAuthenticationId(int authRequestId) {
279 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
280 }
281
282 /**
283 * Gets the dataset id from an authentication id.
284 *
285 * @param authRequestId The authentication id.
286 * @return The dataset id.
287 * @hide
288 */
289 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
290 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
291 }
292
Felipe Leme4753bb02017-03-22 20:24:00 -0700293 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000294
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700295 /**
296 * There is currently no session running.
297 * {@hide}
298 */
299 public static final int NO_SESSION = Integer.MIN_VALUE;
300
Svet Ganov782043c2017-02-11 00:52:02 +0000301 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700302
303 private final Object mLock = new Object();
304
305 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000306 private IAutoFillManagerClient mServiceClient;
307
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700308 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900309 private Cleaner mServiceClientCleaner;
310
311 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800312 private AutofillCallback mCallback;
313
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700314 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000315
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700316 @GuardedBy("mLock")
317 private int mSessionId = NO_SESSION;
318
319 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700320 private int mState = STATE_UNKNOWN;
321
322 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000323 private boolean mEnabled;
324
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700325 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700326 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700327 @Nullable private ParcelableMap mLastAutofilledData;
328
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700329 /** If view tracking is enabled, contains the tracking state */
330 @GuardedBy("mLock")
331 @Nullable private TrackedViews mTrackedViews;
332
Felipe Leme27e20222017-05-18 15:24:11 -0700333 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
334 @GuardedBy("mLock")
335 @Nullable private ArraySet<AutofillId> mFillableIds;
336
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700337 /** If set, session is commited when the field is clicked. */
338 @GuardedBy("mLock")
339 @Nullable private AutofillId mSaveTriggerId;
340
341 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
342 @GuardedBy("mLock")
343 private boolean mSaveOnFinish;
344
Svet Ganov782043c2017-02-11 00:52:02 +0000345 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800346 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000347 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000348 * Asks the client to start an authentication flow.
349 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700350 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000351 * @param intent The authentication intent.
352 * @param fillInIntent The authentication fill-in intent.
353 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700354 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
355 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800356
357 /**
358 * Tells the client this manager has state to be reset.
359 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700360 void autofillCallbackResetableStateAvailable();
361
362 /**
363 * Request showing the autofill UI.
364 *
365 * @param anchor The real view the UI needs to anchor to.
366 * @param width The width of the fill UI content.
367 * @param height The height of the fill UI content.
368 * @param virtualBounds The bounds of the virtual decendant of the anchor.
369 * @param presenter The presenter that controls the fill UI window.
370 * @return Whether the UI was shown.
371 */
372 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
373 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
374
375 /**
376 * Request hiding the autofill UI.
377 *
378 * @return Whether the UI was hidden.
379 */
380 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700381
382 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700383 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700384 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700385 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700386 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700387 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700388
389 /**
390 * Checks is the client is currently visible as understood by autofill.
391 *
392 * @return {@code true} if the client is currently visible
393 */
394 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700395
396 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700397 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700398 *
Phil Weaver846cda932017-06-15 10:10:06 -0700399 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700400 *
Felipe Leme27e20222017-05-18 15:24:11 -0700401 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700402 */
Phil Weaver846cda932017-06-15 10:10:06 -0700403 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700404
405 /**
406 * Finds a view by traversing the hierarchies of the client.
407 *
Phil Weaver846cda932017-06-15 10:10:06 -0700408 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700409 *
410 * @return The view, or {@code null} if not found
411 */
Phil Weaver846cda932017-06-15 10:10:06 -0700412 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700413
414 /**
415 * Runs the specified action on the UI thread.
416 */
417 void runOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700418
419 /**
420 * Gets the complete component name of this client.
421 */
422 ComponentName getComponentName();
Svet Ganov782043c2017-02-11 00:52:02 +0000423 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800424
425 /**
426 * @hide
427 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800428 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800429 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800430 mService = service;
431 }
432
433 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700434 * Restore state after activity lifecycle
435 *
436 * @param savedInstanceState The state to be restored
437 *
438 * {@hide}
439 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700440 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700441 if (!hasAutofillFeature()) {
442 return;
443 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700444 synchronized (mLock) {
445 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
446
Felipe Lemec24a56a2017-08-03 14:27:57 -0700447 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700448 Log.w(TAG, "New session was started before onCreate()");
449 return;
450 }
451
452 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700453 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700454
455 if (mSessionId != NO_SESSION) {
456 ensureServiceClientAddedIfNeededLocked();
457
458 final AutofillClient client = getClientLocked();
459 if (client != null) {
460 try {
461 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
462 mContext.getActivityToken(), mServiceClient.asBinder());
463
464 if (!sessionWasRestored) {
465 Log.w(TAG, "Session " + mSessionId + " could not be restored");
466 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700467 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700468 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700469 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700470 Log.d(TAG, "session " + mSessionId + " was restored");
471 }
472
473 client.autofillCallbackResetableStateAvailable();
474 }
475 } catch (RemoteException e) {
476 Log.e(TAG, "Could not figure out if there was an autofill session", e);
477 }
478 }
479 }
480 }
481 }
482
483 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700484 * Called once the client becomes visible.
485 *
486 * @see AutofillClient#isVisibleForAutofill()
487 *
488 * {@hide}
489 */
490 public void onVisibleForAutofill() {
491 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700492 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700493 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700494 }
495 }
496 }
497
498 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700499 * Save state before activity lifecycle
500 *
501 * @param outState Place to store the state
502 *
503 * {@hide}
504 */
505 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700506 if (!hasAutofillFeature()) {
507 return;
508 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700509 synchronized (mLock) {
510 if (mSessionId != NO_SESSION) {
511 outState.putInt(SESSION_ID_TAG, mSessionId);
512 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700513 if (mState != STATE_UNKNOWN) {
514 outState.putInt(STATE_TAG, mState);
515 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700516 if (mLastAutofilledData != null) {
517 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
518 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700519 }
520 }
521
522 /**
523 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700524 *
525 * <p>Typically used to determine whether the option to explicitly request autofill should
526 * be offered - see {@link #requestAutofill(View)}.
527 *
528 * @return whether autofill is enabled for the current user.
529 */
530 public boolean isEnabled() {
Felipe Leme17292d12017-10-24 14:03:10 -0700531 if (!hasAutofillFeature() || isDisabledByService()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700532 return false;
533 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700534 synchronized (mLock) {
535 ensureServiceClientAddedIfNeededLocked();
536 return mEnabled;
537 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700538 }
539
540 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700541 * Should always be called from {@link AutofillService#getFillEventHistory()}.
542 *
543 * @hide
544 */
545 @Nullable public FillEventHistory getFillEventHistory() {
546 try {
547 return mService.getFillEventHistory();
548 } catch (RemoteException e) {
549 e.rethrowFromSystemServer();
550 return null;
551 }
552 }
553
554 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700555 * Explicitly requests a new autofill context.
556 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700557 * <p>Normally, the autofill context is automatically started if necessary when
558 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
559 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
560 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700561 *
562 * @param view view requesting the new autofill context.
563 */
564 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700565 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700566 }
567
568 /**
569 * Explicitly requests a new autofill context for virtual views.
570 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700571 * <p>Normally, the autofill context is automatically started if necessary when
572 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
573 * cases where it must be explicitly started. For example, when the virtual view offers an
574 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700575 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700576 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
577 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
578 * the absolute bounds could be calculated by:
579 *
580 * <pre class="prettyprint">
581 * int offset[] = new int[2];
582 * getLocationOnScreen(offset);
583 * Rect absBounds = new Rect(bounds.left + offset[0],
584 * bounds.top + offset[1],
585 * bounds.right + offset[0], bounds.bottom + offset[1]);
586 * </pre>
587 *
588 * @param view the virtual view parent.
589 * @param virtualId id identifying the virtual child inside the parent view.
590 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700591 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700592 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
593 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700594 }
595
Felipe Leme2ac463e2017-03-13 14:06:25 -0700596 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700597 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800598 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700599 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800600 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700601 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700602 notifyViewEntered(view, 0);
603 }
604
Felipe Leme17292d12017-10-24 14:03:10 -0700605 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
606 if (isDisabledByService()) {
607 if (sVerbose) {
608 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
609 + ") on state " + getStateAsStringLocked());
610 }
611 return true;
612 }
613 if (mState == STATE_FINISHED && (flags & FLAG_MANUAL_REQUEST) == 0) {
614 if (sVerbose) {
615 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
616 + ") on state " + getStateAsStringLocked());
617 }
618 return true;
619 }
620 return false;
621 }
622
Felipe Lemed1146422017-04-26 10:17:05 -0700623 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700624 if (!hasAutofillFeature()) {
625 return;
626 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700627 AutofillCallback callback = null;
628 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700629 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
Felipe Lemec7b45292017-09-19 09:06:20 -0700630
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700631 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000632
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700633 if (!mEnabled) {
634 if (mCallback != null) {
635 callback = mCallback;
636 }
637 } else {
638 final AutofillId id = getAutofillId(view);
639 final AutofillValue value = view.getAutofillValue();
640
Felipe Lemec24a56a2017-08-03 14:27:57 -0700641 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700642 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700643 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700644 } else {
645 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700646 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700647 }
Felipe Leme24aae152017-03-15 12:33:01 -0700648 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800649 }
650
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700651 if (callback != null) {
652 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000653 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800654 }
655
656 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700657 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800658 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700659 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800660 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700661 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700662 if (!hasAutofillFeature()) {
663 return;
664 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700665 synchronized (mLock) {
666 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800667
Felipe Lemec24a56a2017-08-03 14:27:57 -0700668 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700669 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800670
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700671 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700672 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700673 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800674 }
675 }
676
677 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700678 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700679 *
680 * @param view {@link View} that was exited.
681 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700682 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700683 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
684 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
685 }
686
687 /**
688 * Called when a virtual view's visibility changed.
689 *
690 * @param view {@link View} that was exited.
691 * @param virtualId id identifying the virtual child inside the parent view.
692 * @param isVisible visible if the view is visible in the view hierarchy.
693 */
694 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
695 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
696 }
697
698 /**
699 * Called when a view/virtual view's visibility changed.
700 *
701 * @param view {@link View} that was exited.
702 * @param virtualId id identifying the virtual child inside the parent view.
703 * @param isVisible visible if the view is visible in the view hierarchy.
704 * @param virtual Whether the view is virtual.
705 */
706 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
707 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700708 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700709 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700710 final AutofillId id = virtual ? getAutofillId(view, virtualId)
711 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700712 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700713 if (mFillableIds.contains(id)) {
714 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
715 requestHideFillUi(id, view);
716 }
717 }
718 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700719 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700720 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700721 }
722 }
723 }
724
725 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700726 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800727 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700728 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
729 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
730 * the absolute bounds could be calculated by:
731 *
732 * <pre class="prettyprint">
733 * int offset[] = new int[2];
734 * getLocationOnScreen(offset);
735 * Rect absBounds = new Rect(bounds.left + offset[0],
736 * bounds.top + offset[1],
737 * bounds.right + offset[0], bounds.bottom + offset[1]);
738 * </pre>
739 *
740 * @param view the virtual view parent.
741 * @param virtualId id identifying the virtual child inside the parent view.
742 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800743 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700744 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
745 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700746 }
747
Felipe Leme6dcec872017-05-25 11:24:23 -0700748 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700749 if (!hasAutofillFeature()) {
750 return;
751 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700752 AutofillCallback callback = null;
753 synchronized (mLock) {
Felipe Leme17292d12017-10-24 14:03:10 -0700754 if (shouldIgnoreViewEnteredLocked(view, flags)) return;
755
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700756 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000757
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700758 if (!mEnabled) {
759 if (mCallback != null) {
760 callback = mCallback;
761 }
762 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700763 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700764
Felipe Lemec24a56a2017-08-03 14:27:57 -0700765 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700766 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700767 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700768 } else {
769 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700770 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700771 }
Felipe Leme24aae152017-03-15 12:33:01 -0700772 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800773 }
774
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700775 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700776 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700777 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800778 }
779 }
780
781 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700782 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800783 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700784 * @param view the virtual view parent.
785 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800786 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700787 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700788 if (!hasAutofillFeature()) {
789 return;
790 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700791 synchronized (mLock) {
792 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800793
Felipe Lemec24a56a2017-08-03 14:27:57 -0700794 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700795 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800796
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700797 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700798 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700799 }
Svet Ganov782043c2017-02-11 00:52:02 +0000800 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800801 }
802
803 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800804 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800805 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800806 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800807 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700808 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700809 if (!hasAutofillFeature()) {
810 return;
811 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700812 AutofillId id = null;
813 boolean valueWasRead = false;
814 AutofillValue value = null;
815
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700816 synchronized (mLock) {
817 // If the session is gone some fields might still be highlighted, hence we have to
818 // remove the isAutofilled property even if no sessions are active.
819 if (mLastAutofilledData == null) {
820 view.setAutofilled(false);
821 } else {
822 id = getAutofillId(view);
823 if (mLastAutofilledData.containsKey(id)) {
824 value = view.getAutofillValue();
825 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700826
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700827 if (Objects.equals(mLastAutofilledData.get(id), value)) {
828 view.setAutofilled(true);
829 } else {
830 view.setAutofilled(false);
831 mLastAutofilledData.remove(id);
832 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700833 } else {
834 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700835 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700836 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700837
Felipe Lemec24a56a2017-08-03 14:27:57 -0700838 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700839 if (sVerbose && mEnabled) {
840 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
841 + getStateAsStringLocked());
842 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700843 return;
844 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800845
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700846 if (id == null) {
847 id = getAutofillId(view);
848 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700849
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700850 if (!valueWasRead) {
851 value = view.getAutofillValue();
852 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700853
Felipe Leme0aa4c502017-04-26 12:36:01 -0700854 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700855 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800856 }
857
Felipe Lemebab851c2017-02-03 18:45:08 -0800858 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700859 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800860 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700861 * @param view the virtual view parent.
862 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800863 * @param value new value of the child.
864 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700865 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700866 if (!hasAutofillFeature()) {
867 return;
868 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700869 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700870 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700871 return;
872 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800873
Felipe Leme6dcec872017-05-25 11:24:23 -0700874 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700875 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700876 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800877 }
878
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700879
880 /**
881 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
882 *
883 * @hide
884 */
885 public void notifyViewClicked(View view) {
886 final AutofillId id = view.getAutofillId();
887
888 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
889
890 synchronized (mLock) {
891 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
892 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
893 commitLocked();
894 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
895 mContext.getPackageName());
896 }
897 }
898 }
899
900 /**
901 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
902 *
903 * @hide
904 */
905 public void onActivityFinished() {
906 if (!hasAutofillFeature()) {
907 return;
908 }
909 synchronized (mLock) {
910 if (mSaveOnFinish) {
911 commitLocked();
912 } else {
913 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
914 cancelLocked();
915 }
916 }
917 }
918
Felipe Lemebab851c2017-02-03 18:45:08 -0800919 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700920 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800921 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700922 * <p>This method is typically called by {@link View Views} that manage virtual views; for
923 * example, when the view is rendering an {@code HTML} page with a form and virtual views
924 * that represent the HTML elements, it should call this method after the form is submitted and
925 * another page is rendered.
926 *
927 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
928 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800929 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700930 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700931 if (!hasAutofillFeature()) {
932 return;
933 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700934 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700935 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700936 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800937 }
938
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700939 private void commitLocked() {
940 if (!mEnabled && !isActiveLocked()) {
941 return;
942 }
943 finishSessionLocked();
944 }
945
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700946 /**
947 * Called to indicate the current autofill context should be cancelled.
948 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700949 * <p>This method is typically called by {@link View Views} that manage virtual views; for
950 * example, when the view is rendering an {@code HTML} page with a form and virtual views
951 * that represent the HTML elements, it should call this method if the user does not post the
952 * form but moves to another form in this page.
953 *
954 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
955 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700956 */
957 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700958 if (!hasAutofillFeature()) {
959 return;
960 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700961 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700962 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700963 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700964 }
965
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700966 private void cancelLocked() {
967 if (!mEnabled && !isActiveLocked()) {
968 return;
969 }
970 cancelSessionLocked();
971 }
972
Svet Ganovf965b872017-04-28 16:34:02 -0700973 /** @hide */
974 public void disableOwnedAutofillServices() {
975 disableAutofillServices();
976 }
977
Svet Ganovf20a0372017-04-10 17:08:05 -0700978 /**
979 * If the app calling this API has enabled autofill services they
980 * will be disabled.
981 */
Svet Ganovf965b872017-04-28 16:34:02 -0700982 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700983 if (!hasAutofillFeature()) {
984 return;
985 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700986 try {
987 mService.disableOwnedAutofillServices(mContext.getUserId());
988 } catch (RemoteException e) {
989 throw e.rethrowFromSystemServer();
990 }
991 }
992
Felipe Lemedb041182017-04-21 17:33:38 -0700993 /**
994 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
995 * enabled for the current user, or {@code false} otherwise.
996 */
997 public boolean hasEnabledAutofillServices() {
998 if (mService == null) return false;
999
1000 try {
1001 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1002 } catch (RemoteException e) {
1003 throw e.rethrowFromSystemServer();
1004 }
1005 }
1006
1007 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001008 * Returns {@code true} if autofill is supported by the current device and
1009 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001010 *
1011 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1012 * <ol>
1013 * <li>Low-end devices.
1014 * <li>Device policy rules that forbid its usage.
1015 * </ol>
1016 */
1017 public boolean isAutofillSupported() {
1018 if (mService == null) return false;
1019
1020 try {
1021 return mService.isServiceSupported(mContext.getUserId());
1022 } catch (RemoteException e) {
1023 throw e.rethrowFromSystemServer();
1024 }
1025 }
1026
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001027 private AutofillClient getClientLocked() {
Felipe Leme686128e2017-10-17 14:02:20 -07001028 final AutofillClient client = mContext.getAutofillClient();
1029 if (client == null && sDebug) {
1030 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1031 + mContext);
1032 }
1033 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001034 }
1035
1036 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001037 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -07001038 if (!hasAutofillFeature()) {
1039 return;
1040 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001041 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001042 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1043 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1044 // service set the extra and returned RESULT_CANCELED...
1045
Felipe Leme9f9ee252017-04-27 13:56:22 -07001046 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001047
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001048 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001049 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001050 return;
1051 }
1052 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1053 final Bundle responseData = new Bundle();
1054 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001055 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1056 if (newClientState != null) {
1057 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1058 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001059 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001060 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1061 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001062 } catch (RemoteException e) {
1063 Log.e(TAG, "Error delivering authentication result", e);
1064 }
Svet Ganov782043c2017-02-11 00:52:02 +00001065 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001066 }
1067
Felipe Leme640f30a2017-03-06 15:44:06 -08001068 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001069 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001070 }
1071
Felipe Leme6dcec872017-05-25 11:24:23 -07001072 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001073 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001074 }
1075
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001076 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1077 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001078 if (sVerbose) {
1079 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -07001080 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001081 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001082 if (mState != STATE_UNKNOWN && (flags & FLAG_MANUAL_REQUEST) == 0) {
1083 if (sVerbose) {
1084 Log.v(TAG, "not automatically starting session for " + id
1085 + " on state " + getStateAsStringLocked());
1086 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001087 return;
1088 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001089 try {
Felipe Leme17292d12017-10-24 14:03:10 -07001090 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001091 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001092 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001093 mCallback != null, flags, client.getComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001094 if (mSessionId != NO_SESSION) {
1095 mState = STATE_ACTIVE;
1096 }
Svet Ganov17db9dc2017-02-21 19:54:31 -08001097 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001098 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -08001099 }
Svet Ganov782043c2017-02-11 00:52:02 +00001100 } catch (RemoteException e) {
1101 throw e.rethrowFromSystemServer();
1102 }
1103 }
1104
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001105 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001106 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001107
1108 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001109
Svet Ganov782043c2017-02-11 00:52:02 +00001110 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001111 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001112 } catch (RemoteException e) {
1113 throw e.rethrowFromSystemServer();
1114 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001115
Felipe Lemec24a56a2017-08-03 14:27:57 -07001116 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001117 }
1118
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001119 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001120 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001121
1122 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001123
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001124 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001125 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001126 } catch (RemoteException e) {
1127 throw e.rethrowFromSystemServer();
1128 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001129
Svet Ganov48f10a22017-04-26 18:49:30 -07001130 resetSessionLocked();
1131 }
1132
1133 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001134 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001135 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001136 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001137 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001138 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001139 }
1140
Felipe Leme0aa4c502017-04-26 12:36:01 -07001141 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1142 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001143 if (sVerbose && action != ACTION_VIEW_EXITED) {
1144 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1145 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001146 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001147 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1148
Felipe Leme3461d3c2017-01-19 08:54:55 -08001149 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001150 if (restartIfNecessary) {
Felipe Leme17292d12017-10-24 14:03:10 -07001151 final AutofillClient client = getClientLocked();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001152 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1153 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001154 mCallback != null, flags, client.getComponentName(), mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001155 if (newId != mSessionId) {
1156 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1157 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001158 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme7f33cd32017-05-11 10:10:49 -07001159 if (client != null) {
1160 client.autofillCallbackResetableStateAvailable();
1161 }
1162 }
1163 } else {
1164 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1165 mContext.getUserId());
1166 }
1167
Felipe Leme3461d3c2017-01-19 08:54:55 -08001168 } catch (RemoteException e) {
1169 throw e.rethrowFromSystemServer();
1170 }
1171 }
Svet Ganov782043c2017-02-11 00:52:02 +00001172
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001173 private void ensureServiceClientAddedIfNeededLocked() {
1174 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001175 return;
1176 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001177
Svet Ganov782043c2017-02-11 00:52:02 +00001178 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001179 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001180 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001181 final int userId = mContext.getUserId();
1182 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001183 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1184 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1185 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001186 final IAutoFillManager service = mService;
1187 final IAutoFillManagerClient serviceClient = mServiceClient;
1188 mServiceClientCleaner = Cleaner.create(this, () -> {
1189 try {
1190 service.removeClient(serviceClient, userId);
1191 } catch (RemoteException e) {
1192 }
1193 });
Svet Ganov782043c2017-02-11 00:52:02 +00001194 } catch (RemoteException e) {
1195 throw e.rethrowFromSystemServer();
1196 }
1197 }
1198 }
1199
Felipe Lemee6010f22017-03-03 11:19:51 -08001200 /**
1201 * Registers a {@link AutofillCallback} to receive autofill events.
1202 *
1203 * @param callback callback to receive events.
1204 */
1205 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001206 if (!hasAutofillFeature()) {
1207 return;
1208 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001209 synchronized (mLock) {
1210 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001211
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001212 final boolean hadCallback = mCallback != null;
1213 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001214
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001215 if (!hadCallback) {
1216 try {
1217 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1218 } catch (RemoteException e) {
1219 throw e.rethrowFromSystemServer();
1220 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001221 }
1222 }
1223 }
1224
1225 /**
1226 * Unregisters a {@link AutofillCallback} to receive autofill events.
1227 *
1228 * @param callback callback to stop receiving events.
1229 */
1230 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001231 if (!hasAutofillFeature()) {
1232 return;
1233 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001234 synchronized (mLock) {
1235 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001236
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001237 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001238
Felipe Lemee6010f22017-03-03 11:19:51 -08001239 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001240 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001241 } catch (RemoteException e) {
1242 throw e.rethrowFromSystemServer();
1243 }
1244 }
1245 }
1246
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001247 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1248 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1249 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001250 if (anchor == null) {
1251 return;
1252 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001253
1254 AutofillCallback callback = null;
1255 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001256 if (mSessionId == sessionId) {
1257 AutofillClient client = getClientLocked();
1258
1259 if (client != null) {
1260 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1261 anchorBounds, presenter) && mCallback != null) {
1262 callback = mCallback;
1263 }
1264 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001265 }
1266 }
1267
1268 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001269 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001270 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001271 AutofillCallback.EVENT_INPUT_SHOWN);
1272 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001273 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001274 }
1275 }
1276 }
1277
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001278 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1279 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001280 synchronized (mLock) {
1281 if (sessionId == mSessionId) {
1282 AutofillClient client = getClientLocked();
1283 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001284 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001285 }
1286 }
1287 }
1288 }
1289
Felipe Leme51e29da2017-10-24 14:03:10 -07001290 /** @hide */
1291 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1292 /** @hide */
1293 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1294 /** @hide */
1295 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1296 /** @hide */
1297 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1298 /** @hide */
1299 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1300
1301 private void setState(int flags) {
1302 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001303 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001304 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1305 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001306 // Reset the session state
1307 resetSessionLocked();
1308 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001309 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001310 // Reset connection to system
1311 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001312 if (mServiceClientCleaner != null) {
1313 mServiceClientCleaner.clean();
1314 mServiceClientCleaner = null;
1315 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001316 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001317 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001318 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1319 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001320 }
1321
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001322 /**
1323 * Sets a view as autofilled if the current value is the {code targetValue}.
1324 *
1325 * @param view The view that is to be autofilled
1326 * @param targetValue The value we want to fill into view
1327 */
1328 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1329 AutofillValue currentValue = view.getAutofillValue();
1330 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001331 synchronized (mLock) {
1332 if (mLastAutofilledData == null) {
1333 mLastAutofilledData = new ParcelableMap(1);
1334 }
1335 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001336 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001337 view.setAutofilled(true);
1338 }
1339 }
1340
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001341 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001342 synchronized (mLock) {
1343 if (sessionId != mSessionId) {
1344 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001345 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001346
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001347 final AutofillClient client = getClientLocked();
1348 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001349 return;
1350 }
1351
1352 final int itemCount = ids.size();
1353 int numApplied = 0;
1354 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001355 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001356
1357 for (int i = 0; i < itemCount; i++) {
1358 final AutofillId id = ids.get(i);
1359 final AutofillValue value = values.get(i);
1360 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001361 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001362 if (view == null) {
1363 Log.w(TAG, "autofill(): no View with id " + viewId);
1364 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001365 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001366 if (id.isVirtual()) {
1367 if (virtualValues == null) {
1368 // Most likely there will be just one view with virtual children.
1369 virtualValues = new ArrayMap<>(1);
1370 }
1371 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1372 if (valuesByParent == null) {
1373 // We don't know the size yet, but usually it will be just a few fields...
1374 valuesByParent = new SparseArray<>(5);
1375 virtualValues.put(view, valuesByParent);
1376 }
1377 valuesByParent.put(id.getVirtualChildId(), value);
1378 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001379 // Mark the view as to be autofilled with 'value'
1380 if (mLastAutofilledData == null) {
1381 mLastAutofilledData = new ParcelableMap(itemCount - i);
1382 }
1383 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001384
1385 view.autofill(value);
1386
1387 // Set as autofilled if the values match now, e.g. when the value was updated
1388 // synchronously.
1389 // If autofill happens async, the view is set to autofilled in
1390 // notifyValueChanged.
1391 setAutofilledIfValuesIs(view, value);
1392
1393 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001394 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001395 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001396
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001397 if (virtualValues != null) {
1398 for (int i = 0; i < virtualValues.size(); i++) {
1399 final View parent = virtualValues.keyAt(i);
1400 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1401 parent.autofill(childrenValues);
1402 numApplied += childrenValues.size();
1403 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001404 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001405
Felipe Lemeb22d6352017-09-08 20:03:53 -07001406 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1407 .setPackageName(mContext.getPackageName())
1408 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1409 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001410 mMetricsLogger.write(log);
1411 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001412 }
1413
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001414 /**
1415 * Set the tracked views.
1416 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001417 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001418 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001419 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001420 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001421 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001422 */
Felipe Leme27e20222017-05-18 15:24:11 -07001423 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001424 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1425 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001426 synchronized (mLock) {
1427 if (mEnabled && mSessionId == sessionId) {
1428 if (saveOnAllViewsInvisible) {
1429 mTrackedViews = new TrackedViews(trackedIds);
1430 } else {
1431 mTrackedViews = null;
1432 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001433 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001434 if (fillableIds != null) {
1435 if (mFillableIds == null) {
1436 mFillableIds = new ArraySet<>(fillableIds.length);
1437 }
1438 for (AutofillId id : fillableIds) {
1439 mFillableIds.add(id);
1440 }
1441 if (sVerbose) {
1442 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1443 + ", mFillableIds" + mFillableIds);
1444 }
1445 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001446
1447 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1448 // Turn off trigger on previous view id.
1449 setNotifyOnClickLocked(mSaveTriggerId, false);
1450 }
1451
1452 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1453 // Turn on trigger on new view id.
1454 mSaveTriggerId = saveTriggerId;
1455 setNotifyOnClickLocked(mSaveTriggerId, true);
1456 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001457 }
1458 }
1459 }
1460
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001461 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1462 final View view = findView(id);
1463 if (view == null) {
1464 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1465 return;
1466 }
1467 view.setNotifyAutofillManagerOnClick(notify);
1468 }
1469
Felipe Lemec24a56a2017-08-03 14:27:57 -07001470 private void setSaveUiState(int sessionId, boolean shown) {
1471 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1472 synchronized (mLock) {
1473 if (mSessionId != NO_SESSION) {
1474 // Race condition: app triggered a new session after the previous session was
1475 // finished but before server called setSaveUiState() - need to cancel the new
1476 // session to avoid further inconsistent behavior.
1477 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1478 + ") called on existing session " + mSessionId + "; cancelling it");
1479 cancelSessionLocked();
1480 }
1481 if (shown) {
1482 mSessionId = sessionId;
1483 mState = STATE_SHOWING_SAVE_UI;
1484 } else {
1485 mSessionId = NO_SESSION;
1486 mState = STATE_UNKNOWN;
1487 }
1488 }
1489 }
1490
Felipe Leme650f7ab2017-09-19 13:08:24 -07001491 /**
1492 * Marks the state of the session as finished.
1493 *
1494 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001495 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1496 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1497 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001498 */
1499 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001500 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001501 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001502 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001503 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001504 }
1505 }
1506
Felipe Leme27e20222017-05-18 15:24:11 -07001507 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001508 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001509 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001510 if (anchor == null) {
1511 return;
1512 }
Felipe Leme27e20222017-05-18 15:24:11 -07001513 requestHideFillUi(id, anchor);
1514 }
1515
1516 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001517
1518 AutofillCallback callback = null;
1519 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001520 // We do not check the session id for two reasons:
1521 // 1. If local and remote session id are off sync the UI would be stuck shown
1522 // 2. There is a race between the user state being destroyed due the fill
1523 // service being uninstalled and the UI being dismissed.
1524 AutofillClient client = getClientLocked();
1525 if (client != null) {
1526 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1527 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001528 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001529 }
1530 }
1531
1532 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001533 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001534 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001535 AutofillCallback.EVENT_INPUT_HIDDEN);
1536 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001537 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001538 }
1539 }
1540 }
1541
Felipe Leme17292d12017-10-24 14:03:10 -07001542 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001543 if (sVerbose) {
1544 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001545 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001546 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001547 final View anchor = findView(id);
1548 if (anchor == null) {
1549 return;
1550 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001551
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001552 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001553 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001554 if (mSessionId == sessionId && getClientLocked() != null) {
1555 callback = mCallback;
1556 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001557 }
1558
1559 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001560 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001561 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001562 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1563 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001564 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001565 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001566 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001567
Felipe Leme17292d12017-10-24 14:03:10 -07001568 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001569 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001570 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001571 }
1572 }
1573
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001574 /**
1575 * Get an array of viewIds from a List of {@link AutofillId}.
1576 *
1577 * @param autofillIds The autofill ids to convert
1578 *
1579 * @return The array of viewIds.
1580 */
Felipe Leme27e20222017-05-18 15:24:11 -07001581 // TODO: move to Helper as static method
1582 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1583 final int numIds = autofillIds.length;
1584 final int[] viewIds = new int[numIds];
1585 for (int i = 0; i < numIds; i++) {
1586 viewIds[i] = autofillIds[i].getViewId();
1587 }
1588
1589 return viewIds;
1590 }
1591
1592 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001593 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1594 final int numIds = autofillIds.size();
1595 final int[] viewIds = new int[numIds];
1596 for (int i = 0; i < numIds; i++) {
1597 viewIds[i] = autofillIds.get(i).getViewId();
1598 }
1599
1600 return viewIds;
1601 }
1602
1603 /**
1604 * Find a single view by its id.
1605 *
1606 * @param autofillId The autofill id of the view
1607 *
1608 * @return The view or {@code null} if view was not found
1609 */
1610 private View findView(@NonNull AutofillId autofillId) {
1611 final AutofillClient client = getClientLocked();
1612
1613 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001614 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001615 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001616
Phil Weaver846cda932017-06-15 10:10:06 -07001617 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001618 }
1619
Felipe Lemebb810922017-04-25 15:54:06 -07001620 /** @hide */
1621 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001622 return mService != null;
1623 }
1624
Felipe Lemec24a56a2017-08-03 14:27:57 -07001625 /** @hide */
1626 public void onPendingSaveUi(int operation, IBinder token) {
1627 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1628
1629 synchronized (mLock) {
1630 try {
1631 mService.onPendingSaveUi(operation, token);
1632 } catch (RemoteException e) {
1633 e.rethrowFromSystemServer();
1634 }
1635 }
1636 }
1637
1638 /** @hide */
1639 public void dump(String outerPrefix, PrintWriter pw) {
1640 pw.print(outerPrefix); pw.println("AutofillManager:");
1641 final String pfx = outerPrefix + " ";
1642 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001643 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001644 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme51e29da2017-10-24 14:03:10 -07001645 pw.print(pfx); pw.print("client: "); pw.println(getClientLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001646 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1647 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1648 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1649 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1650 pw.print(pfx); pw.print("tracked views: ");
1651 if (mTrackedViews == null) {
1652 pw.println("null");
1653 } else {
1654 final String pfx2 = pfx + " ";
1655 pw.println();
1656 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1657 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1658 }
1659 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001660 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1661 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme51e29da2017-10-24 14:03:10 -07001662 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1663 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001664 }
1665
Felipe Lemec7b45292017-09-19 09:06:20 -07001666 private String getStateAsStringLocked() {
1667 switch (mState) {
1668 case STATE_UNKNOWN:
1669 return "STATE_UNKNOWN";
1670 case STATE_ACTIVE:
1671 return "STATE_ACTIVE";
1672 case STATE_FINISHED:
1673 return "STATE_FINISHED";
1674 case STATE_SHOWING_SAVE_UI:
1675 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001676 case STATE_DISABLED_BY_SERVICE:
1677 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001678 default:
1679 return "INVALID:" + mState;
1680 }
1681 }
1682
Felipe Lemec24a56a2017-08-03 14:27:57 -07001683 private boolean isActiveLocked() {
1684 return mState == STATE_ACTIVE;
1685 }
1686
Felipe Leme17292d12017-10-24 14:03:10 -07001687 private boolean isDisabledByService() {
1688 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001689 }
1690
Felipe Leme9876a6f2017-05-30 15:47:28 -07001691 private void post(Runnable runnable) {
1692 final AutofillClient client = getClientLocked();
1693 if (client == null) {
1694 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1695 return;
1696 }
1697 client.runOnUiThread(runnable);
1698 }
1699
Felipe Lemee6010f22017-03-03 11:19:51 -08001700 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001701 * View tracking information. Once all tracked views become invisible the session is finished.
1702 */
1703 private class TrackedViews {
1704 /** Visible tracked views */
1705 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1706
1707 /** Invisible tracked views */
1708 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1709
1710 /**
1711 * Check if set is null or value is in set.
1712 *
1713 * @param set The set or null (== empty set)
1714 * @param value The value that might be in the set
1715 *
1716 * @return {@code true} iff set is not empty and value is in set
1717 */
Felipe Leme27e20222017-05-18 15:24:11 -07001718 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001719 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1720 return set != null && set.contains(value);
1721 }
1722
1723 /**
1724 * Add a value to a set. If set is null, create a new set.
1725 *
1726 * @param set The set or null (== empty set)
1727 * @param valueToAdd The value to add
1728 *
1729 * @return The set including the new value. If set was {@code null}, a set containing only
1730 * the new value.
1731 */
Felipe Leme27e20222017-05-18 15:24:11 -07001732 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001733 @NonNull
1734 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1735 if (set == null) {
1736 set = new ArraySet<>(1);
1737 }
1738
1739 set.add(valueToAdd);
1740
1741 return set;
1742 }
1743
1744 /**
1745 * Remove a value from a set.
1746 *
1747 * @param set The set or null (== empty set)
1748 * @param valueToRemove The value to remove
1749 *
1750 * @return The set without the removed value. {@code null} if set was null, or is empty
1751 * after removal.
1752 */
Felipe Leme27e20222017-05-18 15:24:11 -07001753 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001754 @Nullable
1755 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1756 if (set == null) {
1757 return null;
1758 }
1759
1760 set.remove(valueToRemove);
1761
1762 if (set.isEmpty()) {
1763 return null;
1764 }
1765
1766 return set;
1767 }
1768
1769 /**
1770 * Set the tracked views.
1771 *
1772 * @param trackedIds The views to be tracked
1773 */
Felipe Leme27e20222017-05-18 15:24:11 -07001774 TrackedViews(@Nullable AutofillId[] trackedIds) {
1775 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001776 if (trackedIds != null && client != null) {
1777 final boolean[] isVisible;
1778
1779 if (client.isVisibleForAutofill()) {
1780 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1781 } else {
1782 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001783 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001784 }
1785
Felipe Leme27e20222017-05-18 15:24:11 -07001786 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001787 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001788 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001789
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001790 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001791 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001792 } else {
1793 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1794 }
1795 }
1796 }
1797
Felipe Leme9f9ee252017-04-27 13:56:22 -07001798 if (sVerbose) {
1799 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001800 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1801 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1802 }
1803
1804 if (mVisibleTrackedIds == null) {
1805 finishSessionLocked();
1806 }
1807 }
1808
1809 /**
1810 * Called when a {@link View view's} visibility changes.
1811 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001812 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001813 * @param isVisible visible if the view is visible in the view hierarchy.
1814 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001815 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001816 AutofillClient client = getClientLocked();
1817
Felipe Leme9f9ee252017-04-27 13:56:22 -07001818 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001819 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001820 + isVisible);
1821 }
1822
1823 if (client != null && client.isVisibleForAutofill()) {
1824 if (isVisible) {
1825 if (isInSet(mInvisibleTrackedIds, id)) {
1826 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1827 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1828 }
1829 } else {
1830 if (isInSet(mVisibleTrackedIds, id)) {
1831 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1832 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1833 }
1834 }
1835 }
1836
1837 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001838 if (sVerbose) {
1839 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1840 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001841 finishSessionLocked();
1842 }
1843 }
1844
1845 /**
1846 * Called once the client becomes visible.
1847 *
1848 * @see AutofillClient#isVisibleForAutofill()
1849 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001850 void onVisibleForAutofillLocked() {
1851 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001852 // hence update the visibility state for all views.
1853 AutofillClient client = getClientLocked();
1854 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1855 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1856 if (client != null) {
1857 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001858 final ArrayList<AutofillId> orderedInvisibleIds =
1859 new ArrayList<>(mInvisibleTrackedIds);
1860 final boolean[] isVisible = client.getViewVisibility(
1861 getViewIds(orderedInvisibleIds));
1862
1863 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1864 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1865 final AutofillId id = orderedInvisibleIds.get(i);
1866 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001867 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1868
Felipe Leme9f9ee252017-04-27 13:56:22 -07001869 if (sDebug) {
1870 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001871 }
1872 } else {
1873 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1874 }
1875 }
1876 }
1877
1878 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001879 final ArrayList<AutofillId> orderedVisibleIds =
1880 new ArrayList<>(mVisibleTrackedIds);
1881 final boolean[] isVisible = client.getViewVisibility(
1882 getViewIds(orderedVisibleIds));
1883
1884 final int numVisibleTrackedIds = orderedVisibleIds.size();
1885 for (int i = 0; i < numVisibleTrackedIds; i++) {
1886 final AutofillId id = orderedVisibleIds.get(i);
1887
1888 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001889 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1890 } else {
1891 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1892
Felipe Leme9f9ee252017-04-27 13:56:22 -07001893 if (sDebug) {
1894 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001895 }
1896 }
1897 }
1898 }
1899
1900 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1901 mVisibleTrackedIds = updatedVisibleTrackedIds;
1902 }
1903
1904 if (mVisibleTrackedIds == null) {
1905 finishSessionLocked();
1906 }
1907 }
1908 }
1909
1910 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001911 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08001912 *
1913 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001914 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001915 */
1916 public abstract static class AutofillCallback {
1917
1918 /** @hide */
Felipe Leme3dec72b2017-11-02 13:42:17 -07001919 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN, EVENT_INPUT_UNAVAILABLE})
Felipe Lemee6010f22017-03-03 11:19:51 -08001920 @Retention(RetentionPolicy.SOURCE)
1921 public @interface AutofillEventType {}
1922
1923 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001924 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08001925 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001926 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08001927 * should be hidden upon receiving this event.
1928 */
1929 public static final int EVENT_INPUT_SHOWN = 1;
1930
1931 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001932 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001933 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001934 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08001935 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1936 */
1937 public static final int EVENT_INPUT_HIDDEN = 2;
1938
1939 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001940 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07001941 * autofill is not available.
1942 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001943 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07001944 * to avoid flickering, it could shown it upon receiving this event.
1945 */
1946 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1947
1948 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001949 * Called after a change in the autofill state associated with a view.
1950 *
1951 * @param view view associated with the change.
1952 *
1953 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1954 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001955 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1956 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001957
1958 /**
1959 * Called after a change in the autofill state associated with a virtual view.
1960 *
1961 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07001962 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08001963 *
1964 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1965 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001966 public void onAutofillEvent(@NonNull View view, int virtualId,
1967 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07001968 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001969 }
1970
Felipe Leme640f30a2017-03-06 15:44:06 -08001971 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1972 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001973
Felipe Leme640f30a2017-03-06 15:44:06 -08001974 AutofillManagerClient(AutofillManager autofillManager) {
1975 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001976 }
1977
1978 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07001979 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001980 final AutofillManager afm = mAfm.get();
1981 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001982 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00001983 }
1984 }
1985
1986 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001987 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001988 final AutofillManager afm = mAfm.get();
1989 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001990 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001991 }
1992 }
1993
1994 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001995 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1996 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001997 final AutofillManager afm = mAfm.get();
1998 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001999 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002000 }
2001 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002002
2003 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002004 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2005 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002006 final AutofillManager afm = mAfm.get();
2007 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002008 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2009 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002010 }
2011 }
2012
2013 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002014 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002015 final AutofillManager afm = mAfm.get();
2016 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002017 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002018 }
2019 }
2020
2021 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002022 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002023 final AutofillManager afm = mAfm.get();
2024 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002025 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002026 }
2027 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002028
2029 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002030 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002031 final AutofillManager afm = mAfm.get();
2032 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002033 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002034 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002035 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002036 } catch (IntentSender.SendIntentException e) {
2037 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2038 }
2039 });
2040 }
2041 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002042
2043 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002044 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002045 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2046 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002047 final AutofillManager afm = mAfm.get();
2048 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002049 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2050 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002051 }
2052 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002053
2054 @Override
2055 public void setSaveUiState(int sessionId, boolean shown) {
2056 final AutofillManager afm = mAfm.get();
2057 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002058 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2059 }
2060 }
2061
2062 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002063 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002064 final AutofillManager afm = mAfm.get();
2065 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002066 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002067 }
2068 }
Svet Ganov782043c2017-02-11 00:52:02 +00002069 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002070}