blob: 70fe230ce226cbfbd2342c19055777b1549adbc0 [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 Lemea7de4022019-02-19 17:16:45 -080019import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST;
felipealfd3f8f62018-02-07 11:49:07 +010020import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
21import static android.view.autofill.Helper.sDebug;
22import static android.view.autofill.Helper.sVerbose;
23
Svetoslav Ganov24c90452017-12-27 15:17:14 -080024import android.accessibilityservice.AccessibilityServiceInfo;
Felipe Lemee6010f22017-03-03 11:19:51 -080025import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080026import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080027import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070028import android.annotation.RequiresFeature;
Felipe Leme559e21d2019-01-18 17:57:21 -080029import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060030import android.annotation.SystemService;
Felipe Leme2cab38c2019-01-11 10:39:14 -080031import android.annotation.TestApi;
Felipe Lemea4f39cd2019-02-19 15:08:59 -080032import android.content.AutofillOptions;
Felipe Leme17292d12017-10-24 14:03:10 -070033import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080034import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000035import android.content.Intent;
36import android.content.IntentSender;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080037import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
Felipe Leme3461d3c2017-01-19 08:54:55 -080039import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070040import android.metrics.LogMaker;
Felipe Leme68b22222018-07-24 14:57:01 -070041import android.os.Build;
Svet Ganov782043c2017-02-11 00:52:02 +000042import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070043import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000044import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080045import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070046import android.service.autofill.AutofillService;
47import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080048import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070049import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070050import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080051import android.util.Log;
Felipe Lemece404982018-09-17 09:46:13 -070052import android.util.Slog;
Felipe Leme4753bb02017-03-22 20:24:00 -070053import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080054import android.view.Choreographer;
Dake Gu6a20a192018-02-08 12:09:30 -080055import android.view.KeyEvent;
Felipe Leme3461d3c2017-01-19 08:54:55 -080056import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080057import android.view.accessibility.AccessibilityEvent;
58import android.view.accessibility.AccessibilityManager;
59import android.view.accessibility.AccessibilityNodeInfo;
60import android.view.accessibility.AccessibilityNodeProvider;
61import android.view.accessibility.AccessibilityWindowInfo;
felipealfd3f8f62018-02-07 11:49:07 +010062
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070063import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070064import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070065import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Lemed4e52282018-06-18 13:56:38 -070066import com.android.internal.os.IResultReceiver;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080067import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080068import com.android.internal.util.Preconditions;
Felipe Lemec0c15a32019-01-08 10:53:33 -080069import com.android.internal.util.SyncResultReceiver;
felipealfd3f8f62018-02-07 11:49:07 +010070
Svetoslav Ganov24c90452017-12-27 15:17:14 -080071import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070072
Svetoslav Ganov24c90452017-12-27 15:17:14 -080073import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070074import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080075import java.lang.annotation.Retention;
76import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000077import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070078import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080079import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080080import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000081import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070082import java.util.Objects;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080083import java.util.Set;
Felipe Lemec0c15a32019-01-08 10:53:33 -080084
Felipe Leme67e62092018-02-15 14:47:31 -080085//TODO: use java.lang.ref.Cleaner once Android supports Java 9
felipealfd3f8f62018-02-07 11:49:07 +010086import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080087
Felipe Leme3461d3c2017-01-19 08:54:55 -080088/**
Laura Davis43e75d92018-08-10 15:46:06 -070089 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
90 * integrate with the Autofill Framework lifecycle.
91 *
92 * <p>To learn about using Autofill in your app, read
93 * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
94 *
95 * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070096 *
Felipe Leme744976e2017-07-31 11:34:14 -070097 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
Laura Davis43e75d92018-08-10 15:46:06 -070098 * activity context. The autofill context is created when one of the following methods is called for
Felipe Leme744976e2017-07-31 11:34:14 -070099 * the first time in an activity context, and the current user has an enabled autofill service:
100 *
101 * <ul>
102 * <li>{@link #notifyViewEntered(View)}
103 * <li>{@link #notifyViewEntered(View, int, Rect)}
104 * <li>{@link #requestAutofill(View)}
105 * </ul>
106 *
Laura Davis43e75d92018-08-10 15:46:06 -0700107 * <p>Typically, the context is automatically created when the first view of the activity is
Felipe Leme744976e2017-07-31 11:34:14 -0700108 * focused because {@code View.onFocusChanged()} indirectly calls
109 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
110 * explicitly create it (for example, a custom view developer could offer a contextual menu action
111 * in a text-field view to let users manually request autofill).
112 *
113 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
114 * that represents the view hierarchy by calling
115 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
116 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
117 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
118 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
119 * the hierarchy.
120 *
121 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
122 * parses it looking for views that can be autofilled. If the service finds such views, it returns
123 * a data structure to the Android System containing the following optional info:
124 *
125 * <ul>
126 * <li>Datasets used to autofill subsets of views in the activity.
127 * <li>Id of views that the service can save their values for future autofilling.
128 * </ul>
129 *
130 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700131 * UI associated with the view, when the view is focused on and is part of a dataset.
132 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700133 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700134 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700135 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
136 *
137 * <p>When the service returns ids of savable views, the Android System keeps track of changes
138 * made to these views, so they can be used to determine if the autofill save UI is shown later.
139 *
140 * <p>The context is then finished when one of the following occurs:
141 *
142 * <ul>
143 * <li>{@link #commit()} is called or all savable views are gone.
144 * <li>{@link #cancel()} is called.
145 * </ul>
146 *
147 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700148 * 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 -0700149 * option to Save, the current value of the views is then sent to the autofill service.
150 *
Laura Davis43e75d92018-08-10 15:46:06 -0700151 * <h3 id="additional-notes">Additional notes</h3>
152 *
153 * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800154 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600155@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700156@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800157public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800158
Felipe Leme640f30a2017-03-06 15:44:06 -0800159 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800160
Svet Ganov782043c2017-02-11 00:52:02 +0000161 /**
162 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700163 *
Svet Ganov782043c2017-02-11 00:52:02 +0000164 * <p>
165 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000166 */
167 public static final String EXTRA_ASSIST_STRUCTURE =
168 "android.view.autofill.extra.ASSIST_STRUCTURE";
169
170 /**
171 * Intent extra: The result of an authentication operation. It is
172 * either a fully populated {@link android.service.autofill.FillResponse}
173 * or a fully populated {@link android.service.autofill.Dataset} if
174 * a response or a dataset is being authenticated respectively.
175 *
176 * <p>
177 * Type: {@link android.service.autofill.FillResponse} or a
178 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000179 */
180 public static final String EXTRA_AUTHENTICATION_RESULT =
181 "android.view.autofill.extra.AUTHENTICATION_RESULT";
182
Felipe Leme7320ca92017-03-29 15:09:54 -0700183 /**
184 * Intent extra: The optional extras provided by the
185 * {@link android.service.autofill.AutofillService}.
186 *
187 * <p>For example, when the service responds to a {@link
188 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
189 * a {@code FillResponse} that requires authentication, the Intent that launches the
190 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700191 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700192 *
Felipe Lemea9372382017-10-09 14:42:36 -0700193 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
194 * can also add this bundle to the {@link Intent} set as the
195 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
196 * so the bundle can be recovered later on
197 * {@link android.service.autofill.SaveRequest#getClientState()}.
198 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700199 * <p>
200 * Type: {@link android.os.Bundle}
201 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700202 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600203 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700204
Felipe Lemec24a56a2017-08-03 14:27:57 -0700205 /** @hide */
206 public static final String EXTRA_RESTORE_SESSION_TOKEN =
207 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
208
Felipe Leme284ad1c2018-11-15 18:16:12 -0800209 /**
210 * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
211 *
212 * @hide
213 */
214 public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
215 "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
216
Felipe Lemec24a56a2017-08-03 14:27:57 -0700217 private static final String SESSION_ID_TAG = "android:sessionId";
218 private static final String STATE_TAG = "android:state";
219 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
220
Felipe Leme0aa4c502017-04-26 12:36:01 -0700221 /** @hide */ public static final int ACTION_START_SESSION = 1;
222 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
223 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
224 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800225
Felipe Leme68b22222018-07-24 14:57:01 -0700226 /** @hide */ public static final int NO_LOGGING = 0;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700227 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
228 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
229 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
Felipe Leme68b22222018-07-24 14:57:01 -0700230 /** @hide */
231 public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
232 ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
233 : AutofillManager.NO_LOGGING;
234
235 /** @hide */
236 public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700237
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700238 /** Which bits in an authentication id are used for the dataset id */
239 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
240 /** How many bits in an authentication id are used for the dataset id */
241 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
242 /** @hide The index for an undefined data set */
243 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
244
245 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700246 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
247 *
248 * @hide
249 */
250 public static final int PENDING_UI_OPERATION_CANCEL = 1;
251
252 /**
253 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
254 *
255 * @hide
256 */
257 public static final int PENDING_UI_OPERATION_RESTORE = 2;
258
259 /**
260 * Initial state of the autofill context, set when there is no session (i.e., when
261 * {@link #mSessionId} is {@link #NO_SESSION}).
262 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700263 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
264 * the server.
265 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700266 * @hide
267 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700268 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700269
270 /**
271 * State where the autofill context hasn't been {@link #commit() finished} nor
272 * {@link #cancel() canceled} yet.
273 *
274 * @hide
275 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700276 public static final int STATE_ACTIVE = 1;
277
278 /**
279 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700280 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700281 *
282 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
283 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
284 *
285 * @hide
286 */
287 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700288
289 /**
290 * State where the autofill context has been {@link #commit() finished} but the server still has
291 * a session because the Save UI hasn't been dismissed yet.
292 *
293 * @hide
294 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700295 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700296
297 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700298 * State where the autofill is disabled because the service cannot autofill the activity at all.
299 *
300 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
301 * (and {@link #requestAutofill(View, int, Rect)}).
302 *
303 * @hide
304 */
305 public static final int STATE_DISABLED_BY_SERVICE = 4;
306
307 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700308 * Same as {@link #STATE_UNKNOWN}, but used on
309 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
310 * the URL bar changed on client mode
311 *
312 * @hide
313 */
314 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
315
Felipe Lemed9dc9542018-09-19 11:54:28 -0700316 /**
317 * Same as {@link #STATE_UNKNOWN}, but used on
318 * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
319 * the service failed to fullfil a request.
320 *
321 * @hide
322 */
323 public static final int STATE_UNKNOWN_FAILED = 6;
Felipe Leme0c8ce322018-03-23 13:54:22 -0700324
325 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800326 * Timeout in ms for calls to the field classification service.
327 * @hide
328 */
329 public static final int FC_SERVICE_TIMEOUT = 5000;
330
331 /**
Felipe Lemec0c15a32019-01-08 10:53:33 -0800332 * Timeout for calls to system_server.
333 */
334 private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
335
336 /**
Felipe Leme2cab38c2019-01-11 10:39:14 -0800337 * @hide
338 */
339 @TestApi
340 public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
341
342 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800343 * Disables Augmented Autofill.
344 *
345 * @hide
346 */
347 @TestApi
348 public static final int FLAG_SMART_SUGGESTION_OFF = 0x0;
349
350 /**
Felipe Leme559e21d2019-01-18 17:57:21 -0800351 * Displays the Augment Autofill window using the same mechanism (such as a popup-window
352 * attached to the focused view) as the standard autofill.
353 *
354 * @hide
355 */
356 @TestApi
357 public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
358
Felipe Leme559e21d2019-01-18 17:57:21 -0800359 /** @hide */
Felipe Lemecc510272019-02-07 14:56:27 -0800360 @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM })
Felipe Leme559e21d2019-01-18 17:57:21 -0800361 @Retention(RetentionPolicy.SOURCE)
362 public @interface SmartSuggestionMode {}
363
364 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800365 * {@code DeviceConfig} property used to set which Smart Suggestion modes for Augmented Autofill
366 * are available.
Felipe Leme7841d022019-02-07 13:02:18 -0800367 *
368 * @hide
369 */
370 @TestApi
371 public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
372 "smart_suggestion_supported_modes";
373
Felipe Lemea8209102019-02-21 18:01:47 -0800374 /**
375 * Sets how long (in ms) the augmented autofill service is bound while idle.
376 *
377 * <p>Use {@code 0} to keep it permanently bound.
378 *
379 * @hide
380 */
381 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
382 "augmented_service_idle_unbind_timeout";
383
384 /**
385 * Sets how long (in ms) the augmented autofill service request is killed if not replied.
386 *
387 * @hide
388 */
389 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
390 "augmented_service_request_timeout";
391
Felipe Leme80e7bf12019-02-14 10:56:52 -0800392 /** @hide */
393 public static final int RESULT_OK = 0;
394 /** @hide */
395 public static final int RESULT_CODE_NOT_SERVICE = -1;
396
Felipe Leme7841d022019-02-07 13:02:18 -0800397 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700398 * Makes an authentication id from a request id and a dataset id.
399 *
400 * @param requestId The request id.
401 * @param datasetId The dataset id.
402 * @return The authentication id.
403 * @hide
404 */
405 public static int makeAuthenticationId(int requestId, int datasetId) {
406 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
407 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
408 }
409
410 /**
411 * Gets the request id from an authentication id.
412 *
413 * @param authRequestId The authentication id.
414 * @return The request id.
415 * @hide
416 */
417 public static int getRequestIdFromAuthenticationId(int authRequestId) {
418 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
419 }
420
421 /**
422 * Gets the dataset id from an authentication id.
423 *
424 * @param authRequestId The authentication id.
425 * @return The dataset id.
426 * @hide
427 */
428 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
429 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
430 }
431
Felipe Leme4753bb02017-03-22 20:24:00 -0700432 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000433
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700434 /**
435 * There is currently no session running.
436 * {@hide}
437 */
438 public static final int NO_SESSION = Integer.MIN_VALUE;
439
Svet Ganov782043c2017-02-11 00:52:02 +0000440 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700441
442 private final Object mLock = new Object();
443
444 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000445 private IAutoFillManagerClient mServiceClient;
446
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700447 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900448 private Cleaner mServiceClientCleaner;
449
450 @GuardedBy("mLock")
Felipe Leme284ad1c2018-11-15 18:16:12 -0800451 private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
452
453 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800454 private AutofillCallback mCallback;
455
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700456 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000457
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700458 @GuardedBy("mLock")
459 private int mSessionId = NO_SESSION;
460
461 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700462 private int mState = STATE_UNKNOWN;
463
464 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000465 private boolean mEnabled;
466
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700467 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700468 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700469 @Nullable private ParcelableMap mLastAutofilledData;
470
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700471 /** If view tracking is enabled, contains the tracking state */
472 @GuardedBy("mLock")
473 @Nullable private TrackedViews mTrackedViews;
474
Felipe Leme27e20222017-05-18 15:24:11 -0700475 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
476 @GuardedBy("mLock")
477 @Nullable private ArraySet<AutofillId> mFillableIds;
478
Dake Gub0fa3782018-02-26 12:25:14 -0800479 /** id of last requested autofill ui */
480 @Nullable private AutofillId mIdShownFillUi;
481
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800482 /**
483 * Views that were already "entered" - if they're entered again when the session is not active,
484 * they're ignored
485 * */
486 @GuardedBy("mLock")
487 @Nullable private ArraySet<AutofillId> mEnteredIds;
488
Felipe Lemea7de4022019-02-19 17:16:45 -0800489 /**
490 * Views that were otherwised not important for autofill but triggered a session because the
491 * context is whitelisted for augmented autofill.
492 */
493 @GuardedBy("mLock")
494 @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
495
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700496 /** If set, session is commited when the field is clicked. */
497 @GuardedBy("mLock")
498 @Nullable private AutofillId mSaveTriggerId;
499
Dake Gu67decfa2017-12-27 11:48:08 -0800500 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
501 @GuardedBy("mLock")
502 private boolean mOnInvisibleCalled;
503
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700504 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
505 @GuardedBy("mLock")
506 private boolean mSaveOnFinish;
507
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800508 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
509 @GuardedBy("mLock")
510 private CompatibilityBridge mCompatibilityBridge;
511
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800512 @Nullable
513 private final AutofillOptions mOptions;
514
Svet Ganov782043c2017-02-11 00:52:02 +0000515 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800516 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000517 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000518 * Asks the client to start an authentication flow.
519 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700520 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000521 * @param intent The authentication intent.
522 * @param fillInIntent The authentication fill-in intent.
523 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800524 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700525 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800526
527 /**
528 * Tells the client this manager has state to be reset.
529 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800530 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700531
532 /**
533 * Request showing the autofill UI.
534 *
535 * @param anchor The real view the UI needs to anchor to.
536 * @param width The width of the fill UI content.
537 * @param height The height of the fill UI content.
538 * @param virtualBounds The bounds of the virtual decendant of the anchor.
539 * @param presenter The presenter that controls the fill UI window.
540 * @return Whether the UI was shown.
541 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800542 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700543 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
544
545 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800546 * Dispatch unhandled keyevent from Autofill window
547 * @param anchor The real view the UI needs to anchor to.
548 * @param keyEvent Unhandled KeyEvent from autofill window.
549 */
550 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
551
552 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700553 * Request hiding the autofill UI.
554 *
555 * @return Whether the UI was hidden.
556 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800557 boolean autofillClientRequestHideFillUi();
558
559 /**
560 * Gets whether the fill UI is currenlty being shown.
561 *
562 * @return Whether the fill UI is currently being shown
563 */
564 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700565
566 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700567 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700568 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700569 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700570 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800571 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700572
573 /**
574 * Checks is the client is currently visible as understood by autofill.
575 *
576 * @return {@code true} if the client is currently visible
577 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800578 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700579
580 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800581 * Client might disable enter/exit event e.g. when activity is paused.
582 */
583 boolean isDisablingEnterExitEventForAutofill();
584
585 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700586 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700587 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800588 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700589 *
Felipe Leme27e20222017-05-18 15:24:11 -0700590 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700591 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800592 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
593 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700594
595 /**
596 * Finds a view by traversing the hierarchies of the client.
597 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800598 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700599 *
600 * @return The view, or {@code null} if not found
601 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800602 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
603
604 /**
605 * Finds a view by a11y id in a given client window.
606 *
607 * @param viewId The accessibility id of the views to find
608 * @param windowId The accessibility window id where to search
609 *
610 * @return The view, or {@code null} if not found
611 */
612 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700613
614 /**
615 * Runs the specified action on the UI thread.
616 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800617 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700618
619 /**
620 * Gets the complete component name of this client.
621 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800622 ComponentName autofillClientGetComponentName();
623
624 /**
625 * Gets the activity token
626 */
627 @Nullable IBinder autofillClientGetActivityToken();
628
629 /**
630 * @return Whether compatibility mode is enabled.
631 */
Felipe Leme42b97932018-02-20 13:04:31 -0800632 boolean autofillClientIsCompatibilityModeEnabled();
633
634 /**
635 * Gets the next unique autofill ID.
636 *
637 * <p>Typically used to manage views whose content is recycled - see
638 * {@link View#setAutofillId(AutofillId)} for more info.
639 *
640 * @return An ID that is unique in the activity.
641 */
642 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000643 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800644
645 /**
646 * @hide
647 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800648 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800649 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800650 mService = service;
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800651 mOptions = context.getAutofillOptions();
652
653 if (mOptions != null) {
654 sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
655 sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
656 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800657 }
658
659 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800660 * @hide
661 */
662 public void enableCompatibilityMode() {
663 synchronized (mLock) {
664 // The accessibility manager is a singleton so we may need to plug
665 // different bridge based on which activity is currently focused
666 // in the current process. Since compat would be rarely used, just
667 // create and register a new instance every time.
Felipe Lemece404982018-09-17 09:46:13 -0700668 if (sDebug) {
669 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
670 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800671 mCompatibilityBridge = new CompatibilityBridge();
672 }
673 }
674
675 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700676 * Restore state after activity lifecycle
677 *
678 * @param savedInstanceState The state to be restored
679 *
680 * {@hide}
681 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700682 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700683 if (!hasAutofillFeature()) {
684 return;
685 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700686 synchronized (mLock) {
687 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
688
Felipe Lemec24a56a2017-08-03 14:27:57 -0700689 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700690 Log.w(TAG, "New session was started before onCreate()");
691 return;
692 }
693
694 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700695 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700696
697 if (mSessionId != NO_SESSION) {
698 ensureServiceClientAddedIfNeededLocked();
699
Felipe Leme637e05e2017-12-06 12:09:37 -0800700 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700701 if (client != null) {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800702 final SyncResultReceiver receiver = new SyncResultReceiver(
703 SYNC_CALLS_TIMEOUT_MS);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700704 try {
Felipe Lemed4e52282018-06-18 13:56:38 -0700705 mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
706 mServiceClient.asBinder(), receiver);
707 final boolean sessionWasRestored = receiver.getIntResult() == 1;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700708
709 if (!sessionWasRestored) {
710 Log.w(TAG, "Session " + mSessionId + " could not be restored");
711 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700712 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700713 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700714 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700715 Log.d(TAG, "session " + mSessionId + " was restored");
716 }
717
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800718 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700719 }
720 } catch (RemoteException e) {
721 Log.e(TAG, "Could not figure out if there was an autofill session", e);
722 }
723 }
724 }
725 }
726 }
727
728 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700729 * Called once the client becomes visible.
730 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800731 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700732 *
733 * {@hide}
734 */
735 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800736 // This gets called when the client just got visible at which point the visibility
737 // of the tracked views may not have been computed (due to a pending layout, etc).
738 // While generally we have no way to know when the UI has settled. We will evaluate
739 // the tracked views state at the end of next frame to guarantee that everything
740 // that may need to be laid out is laid out.
741 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
742 synchronized (mLock) {
743 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
744 mTrackedViews.onVisibleForAutofillChangedLocked();
745 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700746 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800747 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700748 }
749
750 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800751 * Called once the client becomes invisible.
752 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800753 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800754 *
755 * {@hide}
756 */
757 public void onInvisibleForAutofill() {
758 synchronized (mLock) {
759 mOnInvisibleCalled = true;
760 }
761 }
762
763 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700764 * Save state before activity lifecycle
765 *
766 * @param outState Place to store the state
767 *
768 * {@hide}
769 */
770 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700771 if (!hasAutofillFeature()) {
772 return;
773 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700774 synchronized (mLock) {
775 if (mSessionId != NO_SESSION) {
776 outState.putInt(SESSION_ID_TAG, mSessionId);
777 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700778 if (mState != STATE_UNKNOWN) {
779 outState.putInt(STATE_TAG, mState);
780 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700781 if (mLastAutofilledData != null) {
782 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
783 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700784 }
785 }
786
787 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800788 * @hide
789 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800790 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100791 public boolean isCompatibilityModeEnabledLocked() {
792 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800793 }
794
795 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700796 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700797 *
798 * <p>Typically used to determine whether the option to explicitly request autofill should
799 * be offered - see {@link #requestAutofill(View)}.
800 *
801 * @return whether autofill is enabled for the current user.
802 */
803 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800804 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700805 return false;
806 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700807 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800808 if (isDisabledByServiceLocked()) {
809 return false;
810 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700811 ensureServiceClientAddedIfNeededLocked();
812 return mEnabled;
813 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700814 }
815
816 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700817 * Should always be called from {@link AutofillService#getFillEventHistory()}.
818 *
819 * @hide
820 */
821 @Nullable public FillEventHistory getFillEventHistory() {
822 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800823 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -0700824 mService.getFillEventHistory(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -0800825 return receiver.getParcelableResult();
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700826 } catch (RemoteException e) {
827 e.rethrowFromSystemServer();
828 return null;
829 }
830 }
831
832 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700833 * Explicitly requests a new autofill context.
834 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700835 * <p>Normally, the autofill context is automatically started if necessary when
836 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
837 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
838 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700839 *
840 * @param view view requesting the new autofill context.
841 */
842 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700843 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700844 }
845
846 /**
847 * Explicitly requests a new autofill context for virtual views.
848 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700849 * <p>Normally, the autofill context is automatically started if necessary when
850 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
851 * cases where it must be explicitly started. For example, when the virtual view offers an
852 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700853 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700854 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
855 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
856 * the absolute bounds could be calculated by:
857 *
858 * <pre class="prettyprint">
859 * int offset[] = new int[2];
860 * getLocationOnScreen(offset);
861 * Rect absBounds = new Rect(bounds.left + offset[0],
862 * bounds.top + offset[1],
863 * bounds.right + offset[0], bounds.bottom + offset[1]);
864 * </pre>
865 *
866 * @param view the virtual view parent.
867 * @param virtualId id identifying the virtual child inside the parent view.
868 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700869 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700870 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
871 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700872 }
873
Felipe Leme2ac463e2017-03-13 14:06:25 -0700874 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700875 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800876 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700877 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800878 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700879 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700880 notifyViewEntered(view, 0);
881 }
882
Andreas Gampe3f24e692018-02-05 13:24:28 -0800883 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800884 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800885 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700886 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800887 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
888 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700889 }
890 return true;
891 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800892 if (isFinishedLocked()) {
893 // Session already finished: ignore if automatic request and view already entered
894 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
895 && mEnteredIds.contains(id)) {
896 if (sVerbose) {
897 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
898 + ") on state " + getStateAsStringLocked()
899 + " because view was already entered: " + mEnteredIds);
900 }
901 return true;
902 }
903 }
Felipe Leme17292d12017-10-24 14:03:10 -0700904 return false;
905 }
906
Dake Gu67decfa2017-12-27 11:48:08 -0800907 private boolean isClientVisibleForAutofillLocked() {
908 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800909 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800910 }
911
912 private boolean isClientDisablingEnterExitEvent() {
913 final AutofillClient client = getClient();
914 return client != null && client.isDisablingEnterExitEventForAutofill();
915 }
916
Felipe Lemed1146422017-04-26 10:17:05 -0700917 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700918 if (!hasAutofillFeature()) {
919 return;
920 }
Dake Gu67decfa2017-12-27 11:48:08 -0800921 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700922 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800923 callback = notifyViewEnteredLocked(view, flags);
924 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700925
Dake Gu67decfa2017-12-27 11:48:08 -0800926 if (callback != null) {
927 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
928 }
929 }
Svet Ganov782043c2017-02-11 00:52:02 +0000930
Dake Gu67decfa2017-12-27 11:48:08 -0800931 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800932 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800933 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800934 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800935 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800936
937 AutofillCallback callback = null;
938
939 ensureServiceClientAddedIfNeededLocked();
940
941 if (!mEnabled) {
Felipe Lemef3b844b2018-10-02 13:50:50 -0700942 if (sVerbose) {
943 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
944 }
Dake Gu67decfa2017-12-27 11:48:08 -0800945 if (mCallback != null) {
946 callback = mCallback;
947 }
948 } else {
949 // don't notify entered when Activity is already in background
950 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700951 final AutofillValue value = view.getAutofillValue();
952
Felipe Lemec24a56a2017-08-03 14:27:57 -0700953 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700954 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700955 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700956 } else {
957 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700958 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700959 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800960 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700961 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800962 }
Dake Gu67decfa2017-12-27 11:48:08 -0800963 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800964 }
965
966 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700967 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800968 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700969 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800970 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700971 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700972 if (!hasAutofillFeature()) {
973 return;
974 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700975 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800976 notifyViewExitedLocked(view);
977 }
978 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800979
Andreas Gampe3f24e692018-02-05 13:24:28 -0800980 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800981 void notifyViewExitedLocked(@NonNull View view) {
982 ensureServiceClientAddedIfNeededLocked();
983
984 if (mEnabled && isActiveLocked()) {
985 // dont notify exited when Activity is already in background
986 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -0800987 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800988
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700989 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700990 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700991 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800992 }
993 }
994
995 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700996 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700997 *
998 * @param view {@link View} that was exited.
999 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001000 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001001 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1002 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1003 }
1004
1005 /**
1006 * Called when a virtual view's visibility changed.
1007 *
1008 * @param view {@link View} that was exited.
1009 * @param virtualId id identifying the virtual child inside the parent view.
1010 * @param isVisible visible if the view is visible in the view hierarchy.
1011 */
1012 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1013 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1014 }
1015
1016 /**
1017 * Called when a view/virtual view's visibility changed.
1018 *
1019 * @param view {@link View} that was exited.
1020 * @param virtualId id identifying the virtual child inside the parent view.
1021 * @param isVisible visible if the view is visible in the view hierarchy.
1022 * @param virtual Whether the view is virtual.
1023 */
1024 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1025 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001026 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001027 if (mEnabled && isActiveLocked()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001028 final AutofillId id = virtual ? getAutofillId(view, virtualId)
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001029 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -08001030 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -07001031 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001032 if (mFillableIds.contains(id)) {
1033 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1034 requestHideFillUi(id, view);
1035 }
1036 }
1037 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001038 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -08001039 } else if (sVerbose) {
1040 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -07001041 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001042 }
1043 }
1044 }
1045
1046 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001047 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001048 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001049 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1050 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1051 * the absolute bounds could be calculated by:
1052 *
1053 * <pre class="prettyprint">
1054 * int offset[] = new int[2];
1055 * getLocationOnScreen(offset);
1056 * Rect absBounds = new Rect(bounds.left + offset[0],
1057 * bounds.top + offset[1],
1058 * bounds.right + offset[0], bounds.bottom + offset[1]);
1059 * </pre>
1060 *
1061 * @param view the virtual view parent.
1062 * @param virtualId id identifying the virtual child inside the parent view.
1063 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -08001064 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001065 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1066 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -07001067 }
1068
Felipe Leme6dcec872017-05-25 11:24:23 -07001069 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -07001070 if (!hasAutofillFeature()) {
1071 return;
1072 }
Dake Gu67decfa2017-12-27 11:48:08 -08001073 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001074 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001075 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
1076 }
Felipe Leme17292d12017-10-24 14:03:10 -07001077
Dake Gu67decfa2017-12-27 11:48:08 -08001078 if (callback != null) {
1079 callback.onAutofillEvent(view, virtualId,
1080 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1081 }
1082 }
Svet Ganov782043c2017-02-11 00:52:02 +00001083
Dake Gu67decfa2017-12-27 11:48:08 -08001084 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001085 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001086 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
1087 int flags) {
Felipe Leme305fd402018-09-11 18:20:42 +00001088 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -08001089 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001090 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -08001091
1092 ensureServiceClientAddedIfNeededLocked();
1093
1094 if (!mEnabled) {
Felipe Lemef3b844b2018-10-02 13:50:50 -07001095 if (sVerbose) {
1096 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1097 }
Dake Gu67decfa2017-12-27 11:48:08 -08001098 if (mCallback != null) {
1099 callback = mCallback;
1100 }
1101 } else {
1102 // don't notify entered when Activity is already in background
1103 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001104 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001105 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001106 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001107 } else {
1108 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001109 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001110 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001111 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001112 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001113 }
Dake Gu67decfa2017-12-27 11:48:08 -08001114 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -08001115 }
1116
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001117 @GuardedBy("mLock")
1118 private void addEnteredIdLocked(@NonNull AutofillId id) {
1119 if (mEnteredIds == null) {
1120 mEnteredIds = new ArraySet<>(1);
1121 }
1122 mEnteredIds.add(id);
1123 }
1124
Philip P. Moltmann44611812017-02-23 12:52:46 -08001125 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001126 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001127 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001128 * @param view the virtual view parent.
1129 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001130 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001131 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001132 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001133 if (!hasAutofillFeature()) {
1134 return;
1135 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001136 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001137 notifyViewExitedLocked(view, virtualId);
1138 }
1139 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001140
Andreas Gampe3f24e692018-02-05 13:24:28 -08001141 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001142 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1143 ensureServiceClientAddedIfNeededLocked();
1144
1145 if (mEnabled && isActiveLocked()) {
1146 // don't notify exited when Activity is already in background
1147 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001148 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001149
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001150 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001151 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001152 }
Svet Ganov782043c2017-02-11 00:52:02 +00001153 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001154 }
1155
1156 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001157 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001158 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001159 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001160 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001161 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001162 if (!hasAutofillFeature()) {
1163 return;
1164 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001165 AutofillId id = null;
1166 boolean valueWasRead = false;
1167 AutofillValue value = null;
1168
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001169 synchronized (mLock) {
1170 // If the session is gone some fields might still be highlighted, hence we have to
1171 // remove the isAutofilled property even if no sessions are active.
1172 if (mLastAutofilledData == null) {
1173 view.setAutofilled(false);
1174 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001175 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001176 if (mLastAutofilledData.containsKey(id)) {
1177 value = view.getAutofillValue();
1178 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001179
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001180 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1181 view.setAutofilled(true);
1182 } else {
1183 view.setAutofilled(false);
1184 mLastAutofilledData.remove(id);
1185 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001186 } else {
1187 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001188 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001189 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001190
Felipe Lemec24a56a2017-08-03 14:27:57 -07001191 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001192 if (sVerbose) {
1193 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1194 + "): ignoring on state " + getStateAsStringLocked());
Felipe Lemec7b45292017-09-19 09:06:20 -07001195 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001196 return;
1197 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001198
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001199 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001200 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001201 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001202
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001203 if (!valueWasRead) {
1204 value = view.getAutofillValue();
1205 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001206
Felipe Leme0aa4c502017-04-26 12:36:01 -07001207 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001208 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001209 }
1210
Felipe Lemebab851c2017-02-03 18:45:08 -08001211 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001212 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001213 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001214 * @param view the virtual view parent.
1215 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001216 * @param value new value of the child.
1217 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001218 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001219 if (!hasAutofillFeature()) {
1220 return;
1221 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001222 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001223 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001224 if (sVerbose) {
1225 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1226 + "): ignoring on state " + getStateAsStringLocked());
1227 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001228 return;
1229 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001230
Felipe Leme305fd402018-09-11 18:20:42 +00001231 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001232 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001233 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001234 }
1235
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001236 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001237 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001238 *
Felipe Leme67e62092018-02-15 14:47:31 -08001239 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001240 */
Felipe Leme67e62092018-02-15 14:47:31 -08001241 public void notifyViewClicked(@NonNull View view) {
1242 notifyViewClicked(view.getAutofillId());
1243 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001244
Felipe Leme67e62092018-02-15 14:47:31 -08001245 /**
1246 * Called to indicate a virtual view has been clicked.
1247 *
1248 * @param view the virtual view parent.
1249 * @param virtualId id identifying the virtual child inside the parent view.
1250 */
1251 public void notifyViewClicked(@NonNull View view, int virtualId) {
Felipe Leme305fd402018-09-11 18:20:42 +00001252 notifyViewClicked(getAutofillId(view, virtualId));
Felipe Leme67e62092018-02-15 14:47:31 -08001253 }
1254
1255 private void notifyViewClicked(AutofillId id) {
1256 if (!hasAutofillFeature()) {
1257 return;
1258 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001259 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1260
1261 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001262 if (!mEnabled || !isActiveLocked()) {
1263 return;
1264 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001265 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1266 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1267 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001268 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001269 }
1270 }
1271 }
1272
1273 /**
1274 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1275 *
1276 * @hide
1277 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001278 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001279 if (!hasAutofillFeature()) {
1280 return;
1281 }
1282 synchronized (mLock) {
1283 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001284 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001285 commitLocked();
1286 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001287 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001288 cancelLocked();
1289 }
1290 }
1291 }
1292
Felipe Lemebab851c2017-02-03 18:45:08 -08001293 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001294 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001295 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001296 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1297 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1298 * that represent the HTML elements, it should call this method after the form is submitted and
1299 * another page is rendered.
1300 *
1301 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1302 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001303 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001304 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001305 if (!hasAutofillFeature()) {
1306 return;
1307 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001308 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001309 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001310 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001311 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001312 }
1313
Andreas Gampe3f24e692018-02-05 13:24:28 -08001314 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001315 private void commitLocked() {
1316 if (!mEnabled && !isActiveLocked()) {
1317 return;
1318 }
1319 finishSessionLocked();
1320 }
1321
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001322 /**
1323 * Called to indicate the current autofill context should be cancelled.
1324 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001325 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1326 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1327 * that represent the HTML elements, it should call this method if the user does not post the
1328 * form but moves to another form in this page.
1329 *
1330 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1331 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001332 */
1333 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001334 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001335 if (!hasAutofillFeature()) {
1336 return;
1337 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001338 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001339 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001340 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001341 }
1342
Andreas Gampe3f24e692018-02-05 13:24:28 -08001343 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001344 private void cancelLocked() {
1345 if (!mEnabled && !isActiveLocked()) {
1346 return;
1347 }
1348 cancelSessionLocked();
1349 }
1350
Svet Ganovf965b872017-04-28 16:34:02 -07001351 /** @hide */
1352 public void disableOwnedAutofillServices() {
1353 disableAutofillServices();
1354 }
1355
Svet Ganovf20a0372017-04-10 17:08:05 -07001356 /**
1357 * If the app calling this API has enabled autofill services they
1358 * will be disabled.
1359 */
Svet Ganovf965b872017-04-28 16:34:02 -07001360 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001361 if (!hasAutofillFeature()) {
1362 return;
1363 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001364 try {
1365 mService.disableOwnedAutofillServices(mContext.getUserId());
1366 } catch (RemoteException e) {
1367 throw e.rethrowFromSystemServer();
1368 }
1369 }
1370
Felipe Lemedb041182017-04-21 17:33:38 -07001371 /**
1372 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1373 * enabled for the current user, or {@code false} otherwise.
1374 */
1375 public boolean hasEnabledAutofillServices() {
1376 if (mService == null) return false;
1377
Felipe Lemec0c15a32019-01-08 10:53:33 -08001378 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001379 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001380 mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
1381 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001382 } catch (RemoteException e) {
1383 throw e.rethrowFromSystemServer();
1384 }
1385 }
1386
1387 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001388 * Returns the component name of the {@link AutofillService} that is enabled for the current
1389 * user.
1390 */
1391 @Nullable
1392 public ComponentName getAutofillServiceComponentName() {
1393 if (mService == null) return null;
1394
Felipe Lemec0c15a32019-01-08 10:53:33 -08001395 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme23c75ff2017-12-14 13:27:44 -08001396 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001397 mService.getAutofillServiceComponentName(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001398 return receiver.getParcelableResult();
Felipe Leme23c75ff2017-12-14 13:27:44 -08001399 } catch (RemoteException e) {
1400 throw e.rethrowFromSystemServer();
1401 }
1402 }
1403
1404 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001405 * Gets the id of the {@link UserData} used for
1406 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1407 *
1408 * <p>This method is useful when the service must check the status of the {@link UserData} in
1409 * the device without fetching the whole object.
1410 *
1411 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1412 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1413 * the user.
1414 *
1415 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1416 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1417 * service for the user.
1418 */
1419 @Nullable public String getUserDataId() {
1420 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001421 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001422 mService.getUserDataId(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001423 return receiver.getStringResult();
Felipe Lemef0baef742018-01-26 14:39:39 -08001424 } catch (RemoteException e) {
1425 e.rethrowFromSystemServer();
1426 return null;
1427 }
1428 }
1429
1430 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001431 * Gets the user data used for
1432 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001433 *
Felipe Leme27f45732017-12-22 09:05:22 -08001434 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1435 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1436 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001437 *
Felipe Leme452886a2017-11-27 13:09:13 -08001438 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1439 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001440 */
Felipe Leme452886a2017-11-27 13:09:13 -08001441 @Nullable public UserData getUserData() {
1442 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001443 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001444 mService.getUserData(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001445 return receiver.getParcelableResult();
Felipe Leme452886a2017-11-27 13:09:13 -08001446 } catch (RemoteException e) {
1447 e.rethrowFromSystemServer();
1448 return null;
1449 }
1450 }
1451
1452 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001453 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001454 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001455 *
1456 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1457 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1458 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001459 */
Felipe Leme452886a2017-11-27 13:09:13 -08001460 public void setUserData(@Nullable UserData userData) {
1461 try {
1462 mService.setUserData(userData);
1463 } catch (RemoteException e) {
1464 e.rethrowFromSystemServer();
1465 }
1466 }
1467
1468 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001469 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1470 * enabled.
1471 *
1472 * <p>As field classification is an expensive operation, it could be disabled, either
1473 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1474 * permanently (for example, because the device is a low-level device).
1475 *
1476 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1477 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1478 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001479 */
Felipe Leme329d0402017-12-06 09:22:43 -08001480 public boolean isFieldClassificationEnabled() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001481 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme329d0402017-12-06 09:22:43 -08001482 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001483 mService.isFieldClassificationEnabled(receiver);
1484 return receiver.getIntResult() == 1;
Felipe Leme329d0402017-12-06 09:22:43 -08001485 } catch (RemoteException e) {
1486 e.rethrowFromSystemServer();
1487 return false;
1488 }
1489 }
1490
1491 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001492 * Gets the name of the default algorithm used for
1493 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1494 *
1495 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1496 * set.
1497 *
1498 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1499 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1500 * the user.
1501 */
1502 @Nullable
1503 public String getDefaultFieldClassificationAlgorithm() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001504 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001505 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001506 mService.getDefaultFieldClassificationAlgorithm(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001507 return receiver.getStringResult();
Felipe Leme27f45732017-12-22 09:05:22 -08001508 } catch (RemoteException e) {
1509 e.rethrowFromSystemServer();
1510 return null;
1511 }
1512 }
1513
1514 /**
1515 * Gets the name of all algorithms currently available for
1516 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1517 *
1518 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001519 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1520 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001521 */
1522 @NonNull
1523 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001524 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001525 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001526 mService.getAvailableFieldClassificationAlgorithms(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001527 final String[] algorithms = receiver.getStringArrayResult();
Felipe Lemee4ac7402018-01-16 19:37:00 -08001528 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001529 } catch (RemoteException e) {
1530 e.rethrowFromSystemServer();
1531 return null;
1532 }
1533 }
1534
1535 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001536 * Returns {@code true} if autofill is supported by the current device and
1537 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001538 *
1539 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1540 * <ol>
1541 * <li>Low-end devices.
1542 * <li>Device policy rules that forbid its usage.
1543 * </ol>
1544 */
1545 public boolean isAutofillSupported() {
1546 if (mService == null) return false;
1547
Felipe Lemec0c15a32019-01-08 10:53:33 -08001548 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001549 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001550 mService.isServiceSupported(mContext.getUserId(), receiver);
1551 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001552 } catch (RemoteException e) {
1553 throw e.rethrowFromSystemServer();
1554 }
1555 }
1556
Felipe Leme637e05e2017-12-06 12:09:37 -08001557 // Note: don't need to use locked suffix because mContext is final.
1558 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001559 final AutofillClient client = mContext.getAutofillClient();
Felipe Leme261e75f2018-12-17 10:15:08 -08001560 if (client == null && sVerbose) {
1561 Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
Felipe Leme686128e2017-10-17 14:02:20 -07001562 + mContext);
1563 }
1564 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001565 }
1566
Dake Gud9dbd272018-03-13 11:38:42 -07001567 /**
1568 * Check if autofill ui is showing, must be called on UI thread.
1569 * @hide
1570 */
1571 public boolean isAutofillUiShowing() {
1572 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001573 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001574 }
1575
Svet Ganov782043c2017-02-11 00:52:02 +00001576 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001577 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001578 if (!hasAutofillFeature()) {
1579 return;
1580 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001581 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001582 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1583 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1584 // service set the extra and returned RESULT_CANCELED...
1585
Felipe Leme1ad95f02018-06-15 13:07:34 -07001586 if (sDebug) {
1587 Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
1588 }
Felipe Lemed633f072017-02-14 10:17:17 -08001589
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001590 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001591 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001592 return;
1593 }
Dake Gu67decfa2017-12-27 11:48:08 -08001594 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1595 // of app activity. We enforce enter event to re-show fill ui in such case.
1596 // CTS example:
1597 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1598 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1599 if (!mOnInvisibleCalled && focusView != null
1600 && focusView.canNotifyAutofillEnterExitEvent()) {
1601 notifyViewExitedLocked(focusView);
1602 notifyViewEnteredLocked(focusView, 0);
1603 }
1604 if (data == null) {
1605 // data is set to null when result is not RESULT_OK
1606 return;
1607 }
1608
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001609 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1610 final Bundle responseData = new Bundle();
1611 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001612 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1613 if (newClientState != null) {
1614 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1615 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001616 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001617 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1618 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001619 } catch (RemoteException e) {
1620 Log.e(TAG, "Error delivering authentication result", e);
1621 }
Svet Ganov782043c2017-02-11 00:52:02 +00001622 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001623 }
1624
Felipe Leme42b97932018-02-20 13:04:31 -08001625 /**
1626 * Gets the next unique autofill ID for the activity context.
1627 *
1628 * <p>Typically used to manage views whose content is recycled - see
1629 * {@link View#setAutofillId(AutofillId)} for more info.
1630 *
1631 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1632 * the {@link Context} associated with this {@link AutofillManager}.
1633 */
1634 @Nullable
1635 public AutofillId getNextAutofillId() {
1636 final AutofillClient client = getClient();
1637 if (client == null) return null;
1638
1639 final AutofillId id = client.autofillClientGetNextAutofillId();
1640
1641 if (id == null && sDebug) {
1642 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1643 }
1644
1645 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001646 }
1647
Felipe Leme305fd402018-09-11 18:20:42 +00001648 private static AutofillId getAutofillId(View parent, int virtualId) {
1649 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001650 }
1651
Andreas Gampe3f24e692018-02-05 13:24:28 -08001652 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001653 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1654 @NonNull AutofillValue value, int flags) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001655 if (mEnteredForAugmentedAutofillIds != null
1656 && mEnteredForAugmentedAutofillIds.contains(id)) {
1657 if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
1658 flags |= FLAG_AUGMENTED_AUTOFILL_REQUEST;
1659 }
Felipe Leme9f9ee252017-04-27 13:56:22 -07001660 if (sVerbose) {
1661 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001662 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001663 + ", compatMode=" + isCompatibilityModeEnabledLocked()
1664 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001665 }
Felipe Leme3103c632017-12-18 15:05:14 -08001666 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001667 if (sVerbose) {
1668 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001669 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001670 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001671 return;
1672 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001673 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001674 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001675 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001676
Felipe Lemec0c15a32019-01-08 10:53:33 -08001677 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001678 mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001679 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme185de722018-02-13 17:25:44 -08001680 mCallback != null, flags, client.autofillClientGetComponentName(),
Felipe Lemed4e52282018-06-18 13:56:38 -07001681 isCompatibilityModeEnabledLocked(), receiver);
1682 mSessionId = receiver.getIntResult();
Felipe Lemec24a56a2017-08-03 14:27:57 -07001683 if (mSessionId != NO_SESSION) {
1684 mState = STATE_ACTIVE;
1685 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001686 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001687 } catch (RemoteException e) {
1688 throw e.rethrowFromSystemServer();
1689 }
1690 }
1691
Andreas Gampe3f24e692018-02-05 13:24:28 -08001692 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001693 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001694 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001695
1696 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001697
Svet Ganov782043c2017-02-11 00:52:02 +00001698 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001699 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001700 } catch (RemoteException e) {
1701 throw e.rethrowFromSystemServer();
1702 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001703
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001704 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001705 }
1706
Andreas Gampe3f24e692018-02-05 13:24:28 -08001707 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001708 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001709 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001710
1711 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001712
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001713 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001714 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001715 } catch (RemoteException e) {
1716 throw e.rethrowFromSystemServer();
1717 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001718
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001719 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001720 }
1721
Andreas Gampe3f24e692018-02-05 13:24:28 -08001722 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001723 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001724 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001725 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001726 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001727 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001728 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001729 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001730 if (resetEnteredIds) {
1731 mEnteredIds = null;
1732 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001733 }
1734
Andreas Gampe3f24e692018-02-05 13:24:28 -08001735 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001736 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1737 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001738 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001739 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1740 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001741 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001742 try {
Felipe Lemed62b5162018-06-06 15:08:25 -07001743 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1744 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -08001745 } catch (RemoteException e) {
1746 throw e.rethrowFromSystemServer();
1747 }
1748 }
Svet Ganov782043c2017-02-11 00:52:02 +00001749
Andreas Gampe3f24e692018-02-05 13:24:28 -08001750 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001751 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001752 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001753 return;
1754 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001755
Svet Ganov782043c2017-02-11 00:52:02 +00001756 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001757 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001758 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001759 final int userId = mContext.getUserId();
Felipe Lemec0c15a32019-01-08 10:53:33 -08001760 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001761 mService.addClient(mServiceClient, userId, receiver);
1762 final int flags = receiver.getIntResult();
Felipe Leme9f9ee252017-04-27 13:56:22 -07001763 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1764 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1765 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001766 final IAutoFillManager service = mService;
1767 final IAutoFillManagerClient serviceClient = mServiceClient;
1768 mServiceClientCleaner = Cleaner.create(this, () -> {
Felipe Leme559e21d2019-01-18 17:57:21 -08001769 // TODO(b/123100811): call service to also remove reference to
Felipe Leme284ad1c2018-11-15 18:16:12 -08001770 // mAugmentedAutofillServiceClient
Koji Fukuiccec6a62017-10-18 17:48:40 +09001771 try {
1772 service.removeClient(serviceClient, userId);
1773 } catch (RemoteException e) {
1774 }
1775 });
Svet Ganov782043c2017-02-11 00:52:02 +00001776 } catch (RemoteException e) {
1777 throw e.rethrowFromSystemServer();
1778 }
1779 }
1780 }
1781
Felipe Lemee6010f22017-03-03 11:19:51 -08001782 /**
1783 * Registers a {@link AutofillCallback} to receive autofill events.
1784 *
1785 * @param callback callback to receive events.
1786 */
1787 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001788 if (!hasAutofillFeature()) {
1789 return;
1790 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001791 synchronized (mLock) {
1792 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001793
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001794 final boolean hadCallback = mCallback != null;
1795 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001796
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001797 if (!hadCallback) {
1798 try {
1799 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1800 } catch (RemoteException e) {
1801 throw e.rethrowFromSystemServer();
1802 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001803 }
1804 }
1805 }
1806
1807 /**
1808 * Unregisters a {@link AutofillCallback} to receive autofill events.
1809 *
1810 * @param callback callback to stop receiving events.
1811 */
1812 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001813 if (!hasAutofillFeature()) {
1814 return;
1815 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001816 synchronized (mLock) {
1817 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001818
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001819 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001820
Felipe Lemee6010f22017-03-03 11:19:51 -08001821 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001822 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001823 } catch (RemoteException e) {
1824 throw e.rethrowFromSystemServer();
1825 }
1826 }
1827 }
1828
Felipe Leme559e21d2019-01-18 17:57:21 -08001829 /**
Felipe Leme7a3c9f52019-02-13 16:32:49 -08001830 * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead.
1831 * @hide
1832 */
1833 @SystemApi
1834 @TestApi
1835 @Deprecated
1836 public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
1837 @Nullable List<ComponentName> activities) {
Felipe Leme80e7bf12019-02-14 10:56:52 -08001838 setAugmentedAutofillWhitelist(toSet(packages), toSet(activities));
1839 }
1840
1841 private <T> ArraySet<T> toSet(@Nullable List<T> set) {
1842 return set == null ? null : new ArraySet<T>(set);
Felipe Leme7a3c9f52019-02-13 16:32:49 -08001843 }
1844
1845 /**
Felipe Leme559e21d2019-01-18 17:57:21 -08001846 * Explicitly limits augmented autofill to the given packages and activities.
1847 *
Felipe Leme559e21d2019-01-18 17:57:21 -08001848 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
1849 *
1850 * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
1851 * apps that uses addresses. For example, if the service wants to support augmented autofill on
1852 * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
1853 * of {@code AddressApp2}, it would call:
1854 * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
1855 * Arrays.asList(new ComponentName("AddressApp2", "act1"),
1856 * new ComponentName("AddressApp2", "act2")));}
1857 *
1858 * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
1859 * service, and it's ignored if the caller isn't it.
1860 *
1861 * @hide
1862 */
1863 @SystemApi
1864 @TestApi
Felipe Leme7a3c9f52019-02-13 16:32:49 -08001865 public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
1866 @Nullable Set<ComponentName> activities) {
Felipe Leme80e7bf12019-02-14 10:56:52 -08001867 if (!hasAutofillFeature()) {
1868 return;
1869 }
1870
1871 final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
1872 final int resultCode;
1873 try {
1874 mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
1875 resultReceiver);
1876 resultCode = resultReceiver.getIntResult();
1877 } catch (RemoteException e) {
1878 throw e.rethrowFromSystemServer();
1879 }
1880 switch (resultCode) {
1881 case RESULT_OK:
1882 return;
1883 case RESULT_CODE_NOT_SERVICE:
1884 throw new SecurityException("caller is not user's Augmented Autofill Service");
1885 default:
1886 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
1887 + resultCode);
1888 }
1889 }
1890
1891 private <T> ArrayList<T> toList(@Nullable Set<T> set) {
1892 return set == null ? null : new ArrayList<T>(set);
Felipe Leme559e21d2019-01-18 17:57:21 -08001893 }
1894
Felipe Lemea7de4022019-02-19 17:16:45 -08001895 /**
1896 * Notifies that a non-autofillable view was entered because the activity is whitelisted for
1897 * augmented autofill.
1898 *
1899 * <p>This method is necessary to set the right flag on start, so the server-side session
1900 * doesn't trigger the standard autofill workflow, but the augmented's instead.
1901 *
1902 * @hide
1903 */
1904 public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
1905 final AutofillId id = view.getAutofillId();
1906 synchronized (mLock) {
1907 if (mEnteredForAugmentedAutofillIds == null) {
1908 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
1909 }
1910 mEnteredForAugmentedAutofillIds.add(id);
1911 }
1912 }
1913
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001914 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1915 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1916 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001917 if (anchor == null) {
1918 return;
1919 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001920
1921 AutofillCallback callback = null;
1922 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001923 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001924 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001925
1926 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001927 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08001928 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001929 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08001930 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001931 }
1932 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001933 }
1934 }
1935
1936 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08001937 if (id.isVirtualInt()) {
1938 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001939 AutofillCallback.EVENT_INPUT_SHOWN);
1940 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001941 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001942 }
1943 }
1944 }
1945
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001946 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1947 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001948 synchronized (mLock) {
1949 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001950 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001951 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001952 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1953 // before onAuthenticationResult()
1954 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001955 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001956 }
1957 }
1958 }
1959 }
1960
Dake Gu6a20a192018-02-08 12:09:30 -08001961 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
1962 final View anchor = findView(id);
1963 if (anchor == null) {
1964 return;
1965 }
1966
Dake Gu6a20a192018-02-08 12:09:30 -08001967 synchronized (mLock) {
1968 if (mSessionId == sessionId) {
1969 AutofillClient client = getClient();
1970
1971 if (client != null) {
1972 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
1973 }
1974 }
1975 }
1976 }
1977
Felipe Leme51e29da2017-10-24 14:03:10 -07001978 /** @hide */
1979 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1980 /** @hide */
1981 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1982 /** @hide */
1983 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1984 /** @hide */
1985 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1986 /** @hide */
1987 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1988
1989 private void setState(int flags) {
1990 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001991 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001992 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1993 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001994 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001995 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001996 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001997 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001998 // Reset connection to system
1999 mServiceClient = null;
Felipe Leme284ad1c2018-11-15 18:16:12 -08002000 mAugmentedAutofillServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09002001 if (mServiceClientCleaner != null) {
2002 mServiceClientCleaner.clean();
2003 mServiceClientCleaner = null;
2004 }
Svet Ganov48f10a22017-04-26 18:49:30 -07002005 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002006 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002007 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2008 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002009 }
2010
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002011 /**
2012 * Sets a view as autofilled if the current value is the {code targetValue}.
2013 *
2014 * @param view The view that is to be autofilled
2015 * @param targetValue The value we want to fill into view
2016 */
2017 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
2018 AutofillValue currentValue = view.getAutofillValue();
2019 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002020 synchronized (mLock) {
2021 if (mLastAutofilledData == null) {
2022 mLastAutofilledData = new ParcelableMap(1);
2023 }
Felipe Leme42b97932018-02-20 13:04:31 -08002024 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002025 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002026 view.setAutofilled(true);
2027 }
2028 }
2029
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002030 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002031 synchronized (mLock) {
2032 if (sessionId != mSessionId) {
2033 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07002034 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002035
Felipe Leme637e05e2017-12-06 12:09:37 -08002036 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002037 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002038 return;
2039 }
2040
2041 final int itemCount = ids.size();
2042 int numApplied = 0;
2043 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002044 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
2045 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002046
Felipe Leme49f08ed2018-03-26 16:18:45 -07002047 ArrayList<AutofillId> failedIds = null;
2048
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002049 for (int i = 0; i < itemCount; i++) {
2050 final AutofillId id = ids.get(i);
2051 final AutofillValue value = values.get(i);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002052 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002053 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07002054 // Most likely view has been removed after the initial request was sent to the
2055 // the service; this is fine, but we need to update the view status in the
2056 // server side so it can be triggered again.
2057 Log.d(TAG, "autofill(): no View with id " + id);
2058 if (failedIds == null) {
2059 failedIds = new ArrayList<>();
2060 }
2061 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002062 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07002063 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002064 if (id.isVirtualInt()) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002065 if (virtualValues == null) {
2066 // Most likely there will be just one view with virtual children.
2067 virtualValues = new ArrayMap<>(1);
2068 }
2069 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
2070 if (valuesByParent == null) {
2071 // We don't know the size yet, but usually it will be just a few fields...
2072 valuesByParent = new SparseArray<>(5);
2073 virtualValues.put(view, valuesByParent);
2074 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002075 valuesByParent.put(id.getVirtualChildIntId(), value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002076 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002077 // Mark the view as to be autofilled with 'value'
2078 if (mLastAutofilledData == null) {
2079 mLastAutofilledData = new ParcelableMap(itemCount - i);
2080 }
2081 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002082
2083 view.autofill(value);
2084
2085 // Set as autofilled if the values match now, e.g. when the value was updated
2086 // synchronously.
2087 // If autofill happens async, the view is set to autofilled in
2088 // notifyValueChanged.
2089 setAutofilledIfValuesIs(view, value);
2090
2091 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002092 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002093 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002094
Felipe Leme49f08ed2018-03-26 16:18:45 -07002095 if (failedIds != null) {
2096 if (sVerbose) {
2097 Log.v(TAG, "autofill(): total failed views: " + failedIds);
2098 }
2099 try {
2100 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
2101 } catch (RemoteException e) {
2102 // In theory, we could ignore this error since it's not a big deal, but
2103 // in reality, we rather crash the app anyways, as the failure could be
2104 // a consequence of something going wrong on the server side...
2105 e.rethrowFromSystemServer();
2106 }
2107 }
2108
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002109 if (virtualValues != null) {
2110 for (int i = 0; i < virtualValues.size(); i++) {
2111 final View parent = virtualValues.keyAt(i);
2112 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
2113 parent.autofill(childrenValues);
2114 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07002115 // TODO: we should provide a callback so the parent can call failures; something
2116 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002117 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002118 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002119
Felipe Leme11166522018-05-07 10:18:47 -07002120 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07002121 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07002122 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002123 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002124 }
2125
Felipe Leme11166522018-05-07 10:18:47 -07002126 private LogMaker newLog(int category) {
Felipe Lemeb838a092018-05-22 14:56:15 -07002127 final LogMaker log = new LogMaker(category)
Felipe Leme833c99b2018-05-24 10:41:48 -07002128 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
2129
2130 if (isCompatibilityModeEnabledLocked()) {
2131 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
2132 }
Felipe Lemeb838a092018-05-22 14:56:15 -07002133 final AutofillClient client = getClient();
2134 if (client == null) {
2135 // Client should never be null here, but it doesn't hurt to check...
2136 log.setPackageName(mContext.getPackageName());
2137 } else {
2138 log.setComponentName(client.autofillClientGetComponentName());
2139 }
2140 return log;
Felipe Leme11166522018-05-07 10:18:47 -07002141 }
2142
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002143 /**
2144 * Set the tracked views.
2145 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002146 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002147 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002148 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07002149 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002150 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002151 */
Felipe Leme27e20222017-05-18 15:24:11 -07002152 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002153 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
2154 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002155 synchronized (mLock) {
2156 if (mEnabled && mSessionId == sessionId) {
2157 if (saveOnAllViewsInvisible) {
2158 mTrackedViews = new TrackedViews(trackedIds);
2159 } else {
2160 mTrackedViews = null;
2161 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002162 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07002163 if (fillableIds != null) {
2164 if (mFillableIds == null) {
2165 mFillableIds = new ArraySet<>(fillableIds.length);
2166 }
2167 for (AutofillId id : fillableIds) {
2168 mFillableIds.add(id);
2169 }
2170 if (sVerbose) {
Felipe Leme27e1bf32018-07-10 15:45:43 -07002171 Log.v(TAG, "setTrackedViews(): fillableIds=" + Arrays.toString(fillableIds)
Felipe Leme27e20222017-05-18 15:24:11 -07002172 + ", mFillableIds" + mFillableIds);
2173 }
2174 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002175
2176 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
2177 // Turn off trigger on previous view id.
2178 setNotifyOnClickLocked(mSaveTriggerId, false);
2179 }
2180
2181 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
2182 // Turn on trigger on new view id.
2183 mSaveTriggerId = saveTriggerId;
2184 setNotifyOnClickLocked(mSaveTriggerId, true);
2185 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002186 }
2187 }
2188 }
2189
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002190 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
2191 final View view = findView(id);
2192 if (view == null) {
2193 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
2194 return;
2195 }
2196 view.setNotifyAutofillManagerOnClick(notify);
2197 }
2198
Felipe Lemec24a56a2017-08-03 14:27:57 -07002199 private void setSaveUiState(int sessionId, boolean shown) {
2200 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
2201 synchronized (mLock) {
2202 if (mSessionId != NO_SESSION) {
2203 // Race condition: app triggered a new session after the previous session was
2204 // finished but before server called setSaveUiState() - need to cancel the new
2205 // session to avoid further inconsistent behavior.
2206 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
2207 + ") called on existing session " + mSessionId + "; cancelling it");
2208 cancelSessionLocked();
2209 }
2210 if (shown) {
2211 mSessionId = sessionId;
2212 mState = STATE_SHOWING_SAVE_UI;
2213 } else {
2214 mSessionId = NO_SESSION;
2215 mState = STATE_UNKNOWN;
2216 }
2217 }
2218 }
2219
Felipe Leme650f7ab2017-09-19 13:08:24 -07002220 /**
2221 * Marks the state of the session as finished.
2222 *
2223 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002224 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2225 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
Felipe Lemed9dc9542018-09-19 11:54:28 -07002226 * changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
2227 * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
2228 * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
2229 * service disabled further autofill requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07002230 */
2231 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002232 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002233 if (sVerbose) {
2234 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
2235 + getStateAsString(newState));
2236 }
Felipe Lemed9dc9542018-09-19 11:54:28 -07002237 if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002238 resetSessionLocked(/* resetEnteredIds= */ true);
2239 mState = STATE_UNKNOWN;
2240 } else {
2241 resetSessionLocked(/* resetEnteredIds= */ false);
2242 mState = newState;
2243 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002244 }
2245 }
2246
Felipe Leme284ad1c2018-11-15 18:16:12 -08002247 /**
2248 * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
2249 *
2250 * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
2251 * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
2252 */
2253 private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
2254 synchronized (mLock) {
2255 if (mAugmentedAutofillServiceClient == null) {
2256 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
2257 }
2258 final Bundle resultData = new Bundle();
2259 resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
2260 mAugmentedAutofillServiceClient.asBinder());
2261
2262 try {
2263 result.send(0, resultData);
2264 } catch (RemoteException e) {
2265 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
2266 }
2267 }
2268 }
2269
Dake Gub0fa3782018-02-26 12:25:14 -08002270 /** @hide */
2271 public void requestHideFillUi() {
2272 requestHideFillUi(mIdShownFillUi, true);
2273 }
2274
2275 private void requestHideFillUi(AutofillId id, boolean force) {
2276 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002277 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002278 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002279 if (force) {
2280 // When user taps outside autofill window, force to close fill ui even id does
2281 // not match.
2282 AutofillClient client = getClient();
2283 if (client != null) {
2284 client.autofillClientRequestHideFillUi();
2285 }
2286 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002287 return;
2288 }
Felipe Leme27e20222017-05-18 15:24:11 -07002289 requestHideFillUi(id, anchor);
2290 }
2291
2292 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002293
2294 AutofillCallback callback = null;
2295 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002296 // We do not check the session id for two reasons:
2297 // 1. If local and remote session id are off sync the UI would be stuck shown
2298 // 2. There is a race between the user state being destroyed due the fill
2299 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002300 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002301 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002302 if (client.autofillClientRequestHideFillUi()) {
2303 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002304 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002305 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002306 }
2307 }
2308
2309 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002310 if (id.isVirtualInt()) {
2311 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002312 AutofillCallback.EVENT_INPUT_HIDDEN);
2313 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002314 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002315 }
2316 }
2317 }
2318
Felipe Leme17292d12017-10-24 14:03:10 -07002319 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002320 if (sVerbose) {
2321 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002322 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002323 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002324 final View anchor = findView(id);
2325 if (anchor == null) {
2326 return;
2327 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002328
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002329 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002330 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002331 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002332 callback = mCallback;
2333 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002334 }
2335
2336 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002337 if (id.isVirtualInt()) {
2338 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002339 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2340 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002341 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002342 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002343 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002344
Felipe Leme17292d12017-10-24 14:03:10 -07002345 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002346 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07002347 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07002348 }
2349 }
2350
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002351 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002352 * Find a single view by its id.
2353 *
2354 * @param autofillId The autofill id of the view
2355 *
2356 * @return The view or {@code null} if view was not found
2357 */
2358 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002359 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002360 if (client != null) {
2361 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002362 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002363 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002364 }
2365
Felipe Lemebb810922017-04-25 15:54:06 -07002366 /** @hide */
2367 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002368 return mService != null;
2369 }
2370
Felipe Lemec24a56a2017-08-03 14:27:57 -07002371 /** @hide */
2372 public void onPendingSaveUi(int operation, IBinder token) {
2373 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2374
2375 synchronized (mLock) {
2376 try {
2377 mService.onPendingSaveUi(operation, token);
2378 } catch (RemoteException e) {
2379 e.rethrowFromSystemServer();
2380 }
2381 }
2382 }
2383
2384 /** @hide */
2385 public void dump(String outerPrefix, PrintWriter pw) {
2386 pw.print(outerPrefix); pw.println("AutofillManager:");
2387 final String pfx = outerPrefix + " ";
2388 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002389 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002390 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Lemeb546ca72018-08-15 08:44:12 -07002391 final AutofillClient client = getClient();
2392 if (client != null) {
2393 pw.print(pfx); pw.print("client: "); pw.print(client);
2394 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2395 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002396 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
2397 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2398 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002399 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002400 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
Felipe Lemef5e26302018-06-08 16:26:39 -07002401 pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002402 pw.print(pfx); pw.print("tracked views: ");
2403 if (mTrackedViews == null) {
2404 pw.println("null");
2405 } else {
2406 final String pfx2 = pfx + " ";
2407 pw.println();
2408 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2409 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2410 }
2411 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002412 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Lemea7de4022019-02-19 17:16:45 -08002413 if (mEnteredForAugmentedAutofillIds != null) {
2414 pw.print(pfx); pw.print("entered ids for augmented autofill: ");
2415 pw.println(mEnteredForAugmentedAutofillIds);
2416 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002417 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2418 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Lemea4f39cd2019-02-19 15:08:59 -08002419 if (mOptions != null) {
2420 pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
2421 }
Felipe Lemeb546ca72018-08-15 08:44:12 -07002422 pw.print(pfx); pw.print("compat mode enabled: ");
2423 synchronized (mLock) {
2424 if (mCompatibilityBridge != null) {
2425 final String pfx2 = pfx + " ";
2426 pw.println("true");
2427 pw.print(pfx2); pw.print("windowId: ");
2428 pw.println(mCompatibilityBridge.mFocusedWindowId);
2429 pw.print(pfx2); pw.print("nodeId: ");
2430 pw.println(mCompatibilityBridge.mFocusedNodeId);
2431 pw.print(pfx2); pw.print("virtualId: ");
2432 pw.println(AccessibilityNodeInfo
2433 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2434 pw.print(pfx2); pw.print("focusedBounds: ");
2435 pw.println(mCompatibilityBridge.mFocusedBounds);
2436 } else {
2437 pw.println("false");
2438 }
2439 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002440 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2441 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002442 }
2443
Andreas Gampe3f24e692018-02-05 13:24:28 -08002444 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002445 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002446 return getStateAsString(mState);
2447 }
2448
2449 @NonNull
2450 private static String getStateAsString(int state) {
2451 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002452 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002453 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002454 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002455 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002456 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002457 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002458 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002459 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002460 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002461 return "DISABLED_BY_SERVICE";
2462 case STATE_UNKNOWN_COMPAT_MODE:
2463 return "UNKNOWN_COMPAT_MODE";
Felipe Lemed9dc9542018-09-19 11:54:28 -07002464 case STATE_UNKNOWN_FAILED:
2465 return "UNKNOWN_FAILED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002466 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002467 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002468 }
2469 }
2470
Felipe Leme559e21d2019-01-18 17:57:21 -08002471 /** @hide */
2472 public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
Felipe Lemecc510272019-02-07 14:56:27 -08002473 switch (flags) {
2474 case FLAG_SMART_SUGGESTION_OFF:
2475 return "OFF";
2476 case FLAG_SMART_SUGGESTION_SYSTEM:
2477 return "SYSTEM";
2478 default:
2479 return "INVALID:" + flags;
2480 }
Felipe Leme559e21d2019-01-18 17:57:21 -08002481 }
2482
Andreas Gampe3f24e692018-02-05 13:24:28 -08002483 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002484 private boolean isActiveLocked() {
2485 return mState == STATE_ACTIVE;
2486 }
2487
Andreas Gampe3f24e692018-02-05 13:24:28 -08002488 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002489 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002490 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002491 }
2492
Andreas Gampe3f24e692018-02-05 13:24:28 -08002493 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002494 private boolean isFinishedLocked() {
2495 return mState == STATE_FINISHED;
2496 }
2497
Felipe Leme9876a6f2017-05-30 15:47:28 -07002498 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002499 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002500 if (client == null) {
2501 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2502 return;
2503 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002504 client.autofillClientRunOnUiThread(runnable);
2505 }
2506
2507 /**
2508 * Implementation of the accessibility based compatibility.
2509 */
2510 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2511 @GuardedBy("mLock")
2512 private final Rect mFocusedBounds = new Rect();
2513 @GuardedBy("mLock")
2514 private final Rect mTempBounds = new Rect();
2515
2516 @GuardedBy("mLock")
2517 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2518 @GuardedBy("mLock")
2519 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2520
2521 // Need to report a fake service in case a11y clients check the service list
2522 @NonNull
2523 @GuardedBy("mLock")
2524 AccessibilityServiceInfo mCompatServiceInfo;
2525
2526 CompatibilityBridge() {
2527 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2528 am.setAccessibilityPolicy(this);
2529 }
2530
2531 private AccessibilityServiceInfo getCompatServiceInfo() {
2532 synchronized (mLock) {
2533 if (mCompatServiceInfo != null) {
2534 return mCompatServiceInfo;
2535 }
2536 final Intent intent = new Intent();
2537 intent.setComponent(new ComponentName("android",
2538 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2539 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2540 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2541 try {
2542 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2543 } catch (XmlPullParserException | IOException e) {
2544 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2545 throw new IllegalStateException("Cannot find compat autofill service");
2546 }
2547 return mCompatServiceInfo;
2548 }
2549 }
2550
2551 @Override
2552 public boolean isEnabled(boolean accessibilityEnabled) {
2553 return true;
2554 }
2555
2556 @Override
2557 public int getRelevantEventTypes(int relevantEventTypes) {
2558 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2559 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002560 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002561 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2562 }
2563
2564 @Override
2565 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2566 List<AccessibilityServiceInfo> installedServices) {
2567 if (installedServices == null) {
2568 installedServices = new ArrayList<>();
2569 }
2570 installedServices.add(getCompatServiceInfo());
2571 return installedServices;
2572 }
2573
2574 @Override
2575 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2576 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2577 if (enabledService == null) {
2578 enabledService = new ArrayList<>();
2579 }
2580 enabledService.add(getCompatServiceInfo());
2581 return enabledService;
2582 }
2583
2584 @Override
2585 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2586 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002587 final int type = event.getEventType();
2588 if (sVerbose) {
2589 // NOTE: this is waaay spammy, but that's life.
2590 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme7d4461d2018-08-16 10:49:03 -07002591 + "): virtualId="
2592 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2593 + ", client=" + getClient());
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002594 }
2595 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002596 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2597 synchronized (mLock) {
2598 if (mFocusedWindowId == event.getWindowId()
2599 && mFocusedNodeId == event.getSourceNodeId()) {
2600 return event;
2601 }
2602 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2603 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2604 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2605 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2606 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2607 mFocusedBounds.set(0, 0, 0, 0);
2608 }
2609 final int windowId = event.getWindowId();
2610 final long nodeId = event.getSourceNodeId();
2611 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2612 mFocusedWindowId = windowId;
2613 mFocusedNodeId = nodeId;
2614 }
2615 }
2616 } break;
2617
2618 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2619 synchronized (mLock) {
2620 if (mFocusedWindowId == event.getWindowId()
2621 && mFocusedNodeId == event.getSourceNodeId()) {
2622 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2623 }
2624 }
2625 } break;
2626
Felipe Leme54cc6832018-03-06 12:54:31 -08002627 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2628 synchronized (mLock) {
2629 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2630 }
2631 } break;
2632
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002633 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2634 final AutofillClient client = getClient();
2635 if (client != null) {
2636 synchronized (mLock) {
2637 if (client.autofillClientIsFillUiShowing()) {
2638 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2639 }
2640 updateTrackedViewsLocked();
2641 }
2642 }
2643 } break;
2644 }
2645
2646 return accessibilityEnabled ? event : null;
2647 }
2648
2649 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2650 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2651 if (!isVirtualNode(virtualId)) {
2652 return false;
2653 }
2654 final View view = findViewByAccessibilityId(windowId, nodeId);
2655 if (view == null) {
2656 return false;
2657 }
2658 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2659 if (node == null) {
2660 return false;
2661 }
2662 if (!node.isEditable()) {
2663 return false;
2664 }
2665 final Rect newBounds = mTempBounds;
2666 node.getBoundsInScreen(newBounds);
2667 if (newBounds.equals(focusedBounds)) {
2668 return false;
2669 }
2670 focusedBounds.set(newBounds);
2671 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2672 return true;
2673 }
2674
2675 private void notifyViewExited(int windowId, long nodeId) {
2676 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2677 if (!isVirtualNode(virtualId)) {
2678 return;
2679 }
2680 final View view = findViewByAccessibilityId(windowId, nodeId);
2681 if (view == null) {
2682 return;
2683 }
2684 AutofillManager.this.notifyViewExited(view, virtualId);
2685 }
2686
2687 private void notifyValueChanged(int windowId, long nodeId) {
2688 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2689 if (!isVirtualNode(virtualId)) {
2690 return;
2691 }
2692 final View view = findViewByAccessibilityId(windowId, nodeId);
2693 if (view == null) {
2694 return;
2695 }
2696 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2697 if (node == null) {
2698 return;
2699 }
2700 AutofillManager.this.notifyValueChanged(view, virtualId,
2701 AutofillValue.forText(node.getText()));
2702 }
2703
Felipe Leme54cc6832018-03-06 12:54:31 -08002704 private void notifyViewClicked(int windowId, long nodeId) {
2705 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2706 if (!isVirtualNode(virtualId)) {
2707 return;
2708 }
2709 final View view = findViewByAccessibilityId(windowId, nodeId);
2710 if (view == null) {
2711 return;
2712 }
2713 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2714 if (node == null) {
2715 return;
2716 }
2717 AutofillManager.this.notifyViewClicked(view, virtualId);
2718 }
2719
Andreas Gampe3f24e692018-02-05 13:24:28 -08002720 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002721 private void updateTrackedViewsLocked() {
2722 if (mTrackedViews != null) {
2723 mTrackedViews.onVisibleForAutofillChangedLocked();
2724 }
2725 }
2726
2727 private View findViewByAccessibilityId(int windowId, long nodeId) {
2728 final AutofillClient client = getClient();
2729 if (client == null) {
2730 return null;
2731 }
2732 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2733 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2734 }
2735
2736 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2737 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2738 if (provider == null) {
2739 return null;
2740 }
2741 return provider.createAccessibilityNodeInfo(virtualId);
2742 }
2743
2744 private boolean isVirtualNode(int nodeId) {
2745 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2746 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2747 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002748 }
2749
Felipe Lemee6010f22017-03-03 11:19:51 -08002750 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002751 * View tracking information. Once all tracked views become invisible the session is finished.
2752 */
2753 private class TrackedViews {
2754 /** Visible tracked views */
2755 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2756
2757 /** Invisible tracked views */
2758 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2759
2760 /**
2761 * Check if set is null or value is in set.
2762 *
2763 * @param set The set or null (== empty set)
2764 * @param value The value that might be in the set
2765 *
2766 * @return {@code true} iff set is not empty and value is in set
2767 */
Felipe Leme27e20222017-05-18 15:24:11 -07002768 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002769 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2770 return set != null && set.contains(value);
2771 }
2772
2773 /**
2774 * Add a value to a set. If set is null, create a new set.
2775 *
2776 * @param set The set or null (== empty set)
2777 * @param valueToAdd The value to add
2778 *
2779 * @return The set including the new value. If set was {@code null}, a set containing only
2780 * the new value.
2781 */
Felipe Leme27e20222017-05-18 15:24:11 -07002782 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002783 @NonNull
2784 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2785 if (set == null) {
2786 set = new ArraySet<>(1);
2787 }
2788
2789 set.add(valueToAdd);
2790
2791 return set;
2792 }
2793
2794 /**
2795 * Remove a value from a set.
2796 *
2797 * @param set The set or null (== empty set)
2798 * @param valueToRemove The value to remove
2799 *
2800 * @return The set without the removed value. {@code null} if set was null, or is empty
2801 * after removal.
2802 */
Felipe Leme27e20222017-05-18 15:24:11 -07002803 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002804 @Nullable
2805 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2806 if (set == null) {
2807 return null;
2808 }
2809
2810 set.remove(valueToRemove);
2811
2812 if (set.isEmpty()) {
2813 return null;
2814 }
2815
2816 return set;
2817 }
2818
2819 /**
2820 * Set the tracked views.
2821 *
2822 * @param trackedIds The views to be tracked
2823 */
Felipe Leme27e20222017-05-18 15:24:11 -07002824 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002825 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002826 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002827 final boolean[] isVisible;
2828
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002829 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002830 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002831 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002832 } else {
2833 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002834 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002835 }
2836
Felipe Leme27e20222017-05-18 15:24:11 -07002837 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002838 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002839 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002840
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002841 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002842 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002843 } else {
2844 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2845 }
2846 }
2847 }
2848
Felipe Leme9f9ee252017-04-27 13:56:22 -07002849 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002850 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002851 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2852 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2853 }
2854
2855 if (mVisibleTrackedIds == null) {
2856 finishSessionLocked();
2857 }
2858 }
2859
2860 /**
2861 * Called when a {@link View view's} visibility changes.
2862 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002863 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002864 * @param isVisible visible if the view is visible in the view hierarchy.
2865 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002866 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002867 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002868 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002869 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002870 + isVisible);
2871 }
2872
Dake Gu67decfa2017-12-27 11:48:08 -08002873 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002874 if (isVisible) {
2875 if (isInSet(mInvisibleTrackedIds, id)) {
2876 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2877 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2878 }
2879 } else {
2880 if (isInSet(mVisibleTrackedIds, id)) {
2881 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2882 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2883 }
2884 }
2885 }
2886
2887 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002888 if (sVerbose) {
2889 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2890 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002891 finishSessionLocked();
2892 }
2893 }
2894
2895 /**
2896 * Called once the client becomes visible.
2897 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002898 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002899 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002900 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002901 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002902 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002903 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002904 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002905 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2906 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2907 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07002908 if (sVerbose) {
2909 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
2910 + " vis=" + mVisibleTrackedIds);
2911 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002912 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002913 final ArrayList<AutofillId> orderedInvisibleIds =
2914 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002915 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2916 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002917
2918 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2919 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2920 final AutofillId id = orderedInvisibleIds.get(i);
2921 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002922 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2923
Felipe Leme9f9ee252017-04-27 13:56:22 -07002924 if (sDebug) {
2925 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002926 }
2927 } else {
2928 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2929 }
2930 }
2931 }
2932
2933 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002934 final ArrayList<AutofillId> orderedVisibleIds =
2935 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002936 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2937 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002938
2939 final int numVisibleTrackedIds = orderedVisibleIds.size();
2940 for (int i = 0; i < numVisibleTrackedIds; i++) {
2941 final AutofillId id = orderedVisibleIds.get(i);
2942
2943 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002944 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2945 } else {
2946 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2947
Felipe Leme9f9ee252017-04-27 13:56:22 -07002948 if (sDebug) {
2949 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002950 }
2951 }
2952 }
2953 }
2954
2955 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2956 mVisibleTrackedIds = updatedVisibleTrackedIds;
2957 }
2958
2959 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002960 if (sVerbose) {
2961 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
2962 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002963 finishSessionLocked();
2964 }
2965 }
2966 }
2967
2968 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002969 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002970 *
2971 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002972 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002973 */
2974 public abstract static class AutofillCallback {
2975
2976 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002977 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2978 EVENT_INPUT_SHOWN,
2979 EVENT_INPUT_HIDDEN,
2980 EVENT_INPUT_UNAVAILABLE
2981 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002982 @Retention(RetentionPolicy.SOURCE)
2983 public @interface AutofillEventType {}
2984
2985 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002986 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002987 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002988 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002989 * should be hidden upon receiving this event.
2990 */
2991 public static final int EVENT_INPUT_SHOWN = 1;
2992
2993 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002994 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002995 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002996 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002997 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2998 */
2999 public static final int EVENT_INPUT_HIDDEN = 2;
3000
3001 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003002 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07003003 * autofill is not available.
3004 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003005 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07003006 * to avoid flickering, it could shown it upon receiving this event.
3007 */
3008 public static final int EVENT_INPUT_UNAVAILABLE = 3;
3009
3010 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08003011 * Called after a change in the autofill state associated with a view.
3012 *
3013 * @param view view associated with the change.
3014 *
3015 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3016 */
Felipe Leme81f01d92017-03-16 17:13:25 -07003017 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
3018 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003019
3020 /**
3021 * Called after a change in the autofill state associated with a virtual view.
3022 *
3023 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07003024 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08003025 *
3026 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3027 */
Felipe Leme6dcec872017-05-25 11:24:23 -07003028 public void onAutofillEvent(@NonNull View view, int virtualId,
3029 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07003030 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003031 }
3032
Felipe Leme640f30a2017-03-06 15:44:06 -08003033 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
3034 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00003035
Felipe Leme284ad1c2018-11-15 18:16:12 -08003036 private AutofillManagerClient(AutofillManager autofillManager) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003037 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00003038 }
3039
3040 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07003041 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003042 final AutofillManager afm = mAfm.get();
3043 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07003044 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00003045 }
3046 }
3047
3048 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003049 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003050 final AutofillManager afm = mAfm.get();
3051 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003052 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00003053 }
3054 }
3055
3056 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07003057 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
3058 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003059 final AutofillManager afm = mAfm.get();
3060 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003061 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00003062 }
3063 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003064
3065 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003066 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3067 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003068 final AutofillManager afm = mAfm.get();
3069 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003070 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3071 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07003072 }
3073 }
3074
3075 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003076 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003077 final AutofillManager afm = mAfm.get();
3078 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08003079 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07003080 }
3081 }
3082
3083 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07003084 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003085 final AutofillManager afm = mAfm.get();
3086 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07003087 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08003088 }
3089 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07003090
3091 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08003092 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
3093 final AutofillManager afm = mAfm.get();
3094 if (afm != null) {
3095 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
3096 }
3097 }
3098
3099 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07003100 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003101 final AutofillManager afm = mAfm.get();
3102 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003103 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003104 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07003105 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07003106 } catch (IntentSender.SendIntentException e) {
3107 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
3108 }
3109 });
3110 }
3111 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003112
3113 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07003114 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003115 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
3116 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003117 final AutofillManager afm = mAfm.get();
3118 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003119 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
3120 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003121 }
3122 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07003123
3124 @Override
3125 public void setSaveUiState(int sessionId, boolean shown) {
3126 final AutofillManager afm = mAfm.get();
3127 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003128 afm.post(() -> afm.setSaveUiState(sessionId, shown));
3129 }
3130 }
3131
3132 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07003133 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003134 final AutofillManager afm = mAfm.get();
3135 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07003136 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07003137 }
3138 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003139
3140 @Override
3141 public void getAugmentedAutofillClient(IResultReceiver result) {
3142 final AutofillManager afm = mAfm.get();
3143 if (afm != null) {
3144 afm.post(() -> afm.getAugmentedAutofillClient(result));
3145 }
3146 }
3147 }
3148
3149 private static final class AugmentedAutofillManagerClient
3150 extends IAugmentedAutofillManagerClient.Stub {
3151 private final WeakReference<AutofillManager> mAfm;
3152
3153 private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
3154 mAfm = new WeakReference<>(autofillManager);
3155 }
3156
3157 @Override
3158 public Rect getViewCoordinates(@NonNull AutofillId id) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003159 final AutofillManager afm = mAfm.get();
3160 if (afm == null) return null;
3161
3162 final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
Feng Cao023b84c2018-12-14 15:51:17 -08003163 final Rect windowVisibleDisplayFrame = new Rect();
3164 view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
Felipe Leme284ad1c2018-11-15 18:16:12 -08003165 final int[] location = new int[2];
3166 view.getLocationOnScreen(location);
Feng Cao023b84c2018-12-14 15:51:17 -08003167 final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
3168 location[0] + view.getWidth(),
3169 location[1] - windowVisibleDisplayFrame.top + view.getHeight());
Felipe Leme284ad1c2018-11-15 18:16:12 -08003170 if (sVerbose) {
3171 Log.v(TAG, "Coordinates for " + id + ": " + rect);
3172 }
3173 return rect;
3174 }
3175
3176 @Override
3177 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
3178 final AutofillManager afm = mAfm.get();
3179 if (afm != null) {
3180 afm.post(() -> afm.autofill(sessionId, ids, values));
3181 }
3182 }
Feng Cao158b6562019-01-07 15:42:52 -08003183
3184 @Override
3185 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3186 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3187 final AutofillManager afm = mAfm.get();
3188 if (afm != null) {
3189 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3190 presenter));
3191 }
3192 }
3193
3194 @Override
3195 public void requestHideFillUi(int sessionId, AutofillId id) {
3196 final AutofillManager afm = mAfm.get();
3197 if (afm != null) {
3198 afm.post(() -> afm.requestHideFillUi(id, false));
3199 }
3200 }
Svet Ganov782043c2017-02-11 00:52:02 +00003201 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08003202}