blob: 54bdb886f49bdd44997f7a79daf3715250dec66c [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
felipealfd3f8f62018-02-07 11:49:07 +010019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
Felipe Leme481db7c2019-03-14 15:50:12 -070022import static android.view.autofill.Helper.toList;
felipealfd3f8f62018-02-07 11:49:07 +010023
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;
Joanne Chung9e247b12019-07-19 23:41:32 +080046import android.os.SystemClock;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070047import android.service.autofill.AutofillService;
48import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080049import android.service.autofill.UserData;
lpetere28d6b02019-11-27 15:02:42 +080050import android.text.TextUtils;
Felipe Leme4753bb02017-03-22 20:24:00 -070051import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070052import android.util.ArraySet;
Felipe Lemee5db59d2019-05-07 11:10:07 -070053import android.util.DebugUtils;
Felipe Leme3461d3c2017-01-19 08:54:55 -080054import android.util.Log;
Felipe Lemece404982018-09-17 09:46:13 -070055import android.util.Slog;
Felipe Leme4753bb02017-03-22 20:24:00 -070056import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080057import android.view.Choreographer;
Dake Gu6a20a192018-02-08 12:09:30 -080058import android.view.KeyEvent;
Felipe Leme3461d3c2017-01-19 08:54:55 -080059import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080060import android.view.accessibility.AccessibilityEvent;
61import android.view.accessibility.AccessibilityManager;
62import android.view.accessibility.AccessibilityNodeInfo;
63import android.view.accessibility.AccessibilityNodeProvider;
64import android.view.accessibility.AccessibilityWindowInfo;
lpetere28d6b02019-11-27 15:02:42 +080065import android.widget.EditText;
felipealfd3f8f62018-02-07 11:49:07 +010066
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070067import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070068import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070069import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Lemed4e52282018-06-18 13:56:38 -070070import com.android.internal.os.IResultReceiver;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080071import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080072import com.android.internal.util.Preconditions;
Felipe Lemec0c15a32019-01-08 10:53:33 -080073import com.android.internal.util.SyncResultReceiver;
felipealfd3f8f62018-02-07 11:49:07 +010074
Svetoslav Ganov24c90452017-12-27 15:17:14 -080075import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070076
Svetoslav Ganov24c90452017-12-27 15:17:14 -080077import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070078import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080079import java.lang.annotation.Retention;
80import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000081import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070082import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080083import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080084import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000085import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070086import java.util.Objects;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080087import java.util.Set;
Felipe Lemec0c15a32019-01-08 10:53:33 -080088
felipealfd3f8f62018-02-07 11:49:07 +010089import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080090
Adam He5be0f152020-02-03 15:53:32 -080091//TODO: use java.lang.ref.Cleaner once Android supports Java 9
92
Felipe Leme3461d3c2017-01-19 08:54:55 -080093/**
Laura Davis43e75d92018-08-10 15:46:06 -070094 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
95 * integrate with the Autofill Framework lifecycle.
96 *
97 * <p>To learn about using Autofill in your app, read
98 * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
99 *
100 * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700101 *
Felipe Leme744976e2017-07-31 11:34:14 -0700102 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
Laura Davis43e75d92018-08-10 15:46:06 -0700103 * activity context. The autofill context is created when one of the following methods is called for
Felipe Leme744976e2017-07-31 11:34:14 -0700104 * the first time in an activity context, and the current user has an enabled autofill service:
105 *
106 * <ul>
107 * <li>{@link #notifyViewEntered(View)}
108 * <li>{@link #notifyViewEntered(View, int, Rect)}
109 * <li>{@link #requestAutofill(View)}
110 * </ul>
111 *
Laura Davis43e75d92018-08-10 15:46:06 -0700112 * <p>Typically, the context is automatically created when the first view of the activity is
Felipe Leme744976e2017-07-31 11:34:14 -0700113 * focused because {@code View.onFocusChanged()} indirectly calls
114 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
115 * explicitly create it (for example, a custom view developer could offer a contextual menu action
116 * in a text-field view to let users manually request autofill).
117 *
118 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
119 * that represents the view hierarchy by calling
120 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
121 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
122 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
123 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
124 * the hierarchy.
125 *
126 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
127 * parses it looking for views that can be autofilled. If the service finds such views, it returns
128 * a data structure to the Android System containing the following optional info:
129 *
130 * <ul>
131 * <li>Datasets used to autofill subsets of views in the activity.
132 * <li>Id of views that the service can save their values for future autofilling.
133 * </ul>
134 *
135 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700136 * UI associated with the view, when the view is focused on and is part of a dataset.
137 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700138 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700139 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700140 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
141 *
142 * <p>When the service returns ids of savable views, the Android System keeps track of changes
143 * made to these views, so they can be used to determine if the autofill save UI is shown later.
144 *
145 * <p>The context is then finished when one of the following occurs:
146 *
147 * <ul>
148 * <li>{@link #commit()} is called or all savable views are gone.
149 * <li>{@link #cancel()} is called.
150 * </ul>
151 *
152 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700153 * 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 -0700154 * option to Save, the current value of the views is then sent to the autofill service.
155 *
Laura Davis43e75d92018-08-10 15:46:06 -0700156 * <h3 id="additional-notes">Additional notes</h3>
157 *
158 * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800159 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600160@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700161@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800162public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800163
Felipe Leme640f30a2017-03-06 15:44:06 -0800164 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800165
Svet Ganov782043c2017-02-11 00:52:02 +0000166 /**
167 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700168 *
Svet Ganov782043c2017-02-11 00:52:02 +0000169 * <p>
170 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000171 */
172 public static final String EXTRA_ASSIST_STRUCTURE =
173 "android.view.autofill.extra.ASSIST_STRUCTURE";
174
175 /**
176 * Intent extra: The result of an authentication operation. It is
177 * either a fully populated {@link android.service.autofill.FillResponse}
178 * or a fully populated {@link android.service.autofill.Dataset} if
179 * a response or a dataset is being authenticated respectively.
180 *
181 * <p>
182 * Type: {@link android.service.autofill.FillResponse} or a
183 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000184 */
185 public static final String EXTRA_AUTHENTICATION_RESULT =
186 "android.view.autofill.extra.AUTHENTICATION_RESULT";
187
Felipe Leme7320ca92017-03-29 15:09:54 -0700188 /**
189 * Intent extra: The optional extras provided by the
190 * {@link android.service.autofill.AutofillService}.
191 *
192 * <p>For example, when the service responds to a {@link
193 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
194 * a {@code FillResponse} that requires authentication, the Intent that launches the
195 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700196 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700197 *
Felipe Lemea9372382017-10-09 14:42:36 -0700198 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
199 * can also add this bundle to the {@link Intent} set as the
200 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
201 * so the bundle can be recovered later on
202 * {@link android.service.autofill.SaveRequest#getClientState()}.
203 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700204 * <p>
205 * Type: {@link android.os.Bundle}
206 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700207 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600208 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700209
Felipe Lemec24a56a2017-08-03 14:27:57 -0700210 /** @hide */
211 public static final String EXTRA_RESTORE_SESSION_TOKEN =
212 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
213
TYM Tsai8af721d2019-07-24 17:11:59 +0800214 /** @hide */
215 public static final String EXTRA_RESTORE_CROSS_ACTIVITY =
216 "android.view.autofill.extra.RESTORE_CROSS_ACTIVITY";
217
Felipe Leme284ad1c2018-11-15 18:16:12 -0800218 /**
219 * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
220 *
221 * @hide
222 */
223 public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
224 "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
225
Felipe Lemec24a56a2017-08-03 14:27:57 -0700226 private static final String SESSION_ID_TAG = "android:sessionId";
227 private static final String STATE_TAG = "android:state";
228 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
229
Felipe Leme0aa4c502017-04-26 12:36:01 -0700230 /** @hide */ public static final int ACTION_START_SESSION = 1;
231 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
232 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
233 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
TYM Tsai88398352020-01-14 19:49:05 +0800234 /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800235
Felipe Leme68b22222018-07-24 14:57:01 -0700236 /** @hide */ public static final int NO_LOGGING = 0;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700237 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
238 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
239 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
Felipe Lemedf30f272019-03-19 13:47:48 -0700240 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
Felipe Lemeadb34f52019-03-15 16:24:26 -0700241
Felipe Lemee5db59d2019-05-07 11:10:07 -0700242 // NOTE: flag below is used by the session start receiver only, hence it can have values above
243 /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
Felipe Lemeadb34f52019-03-15 16:24:26 -0700244
Felipe Leme68b22222018-07-24 14:57:01 -0700245 /** @hide */
246 public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
247 ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
248 : AutofillManager.NO_LOGGING;
249
250 /** @hide */
251 public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700252
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700253 /** Which bits in an authentication id are used for the dataset id */
254 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
255 /** How many bits in an authentication id are used for the dataset id */
256 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
257 /** @hide The index for an undefined data set */
258 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
259
260 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700261 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
262 *
263 * @hide
264 */
265 public static final int PENDING_UI_OPERATION_CANCEL = 1;
266
267 /**
268 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
269 *
270 * @hide
271 */
272 public static final int PENDING_UI_OPERATION_RESTORE = 2;
273
274 /**
275 * Initial state of the autofill context, set when there is no session (i.e., when
276 * {@link #mSessionId} is {@link #NO_SESSION}).
277 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700278 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
279 * the server.
280 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700281 * @hide
282 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700283 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700284
285 /**
286 * State where the autofill context hasn't been {@link #commit() finished} nor
287 * {@link #cancel() canceled} yet.
288 *
289 * @hide
290 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700291 public static final int STATE_ACTIVE = 1;
292
293 /**
294 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700295 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700296 *
297 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
298 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
299 *
300 * @hide
301 */
302 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700303
304 /**
305 * State where the autofill context has been {@link #commit() finished} but the server still has
306 * a session because the Save UI hasn't been dismissed yet.
307 *
308 * @hide
309 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700310 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700311
312 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700313 * State where the autofill is disabled because the service cannot autofill the activity at all.
314 *
315 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
316 * (and {@link #requestAutofill(View, int, Rect)}).
317 *
318 * @hide
319 */
320 public static final int STATE_DISABLED_BY_SERVICE = 4;
321
322 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700323 * Same as {@link #STATE_UNKNOWN}, but used on
Felipe Lemeadb34f52019-03-15 16:24:26 -0700324 * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
325 * because the URL bar changed on client mode
Felipe Leme0c8ce322018-03-23 13:54:22 -0700326 *
327 * @hide
328 */
329 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
330
Felipe Lemed9dc9542018-09-19 11:54:28 -0700331 /**
332 * Same as {@link #STATE_UNKNOWN}, but used on
Felipe Lemeadb34f52019-03-15 16:24:26 -0700333 * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
334 * because the service failed to fullfil a request.
Felipe Lemed9dc9542018-09-19 11:54:28 -0700335 *
336 * @hide
337 */
338 public static final int STATE_UNKNOWN_FAILED = 6;
Felipe Leme0c8ce322018-03-23 13:54:22 -0700339
340 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800341 * Timeout in ms for calls to the field classification service.
342 * @hide
343 */
344 public static final int FC_SERVICE_TIMEOUT = 5000;
345
346 /**
Felipe Lemec0c15a32019-01-08 10:53:33 -0800347 * Timeout for calls to system_server.
348 */
349 private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
350
351 /**
Felipe Leme2cab38c2019-01-11 10:39:14 -0800352 * @hide
353 */
354 @TestApi
355 public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
356
357 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800358 * Disables Augmented Autofill.
359 *
360 * @hide
361 */
362 @TestApi
363 public static final int FLAG_SMART_SUGGESTION_OFF = 0x0;
364
365 /**
Felipe Leme559e21d2019-01-18 17:57:21 -0800366 * Displays the Augment Autofill window using the same mechanism (such as a popup-window
367 * attached to the focused view) as the standard autofill.
368 *
369 * @hide
370 */
371 @TestApi
372 public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
373
Felipe Leme559e21d2019-01-18 17:57:21 -0800374 /** @hide */
Felipe Lemecc510272019-02-07 14:56:27 -0800375 @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM })
Felipe Leme559e21d2019-01-18 17:57:21 -0800376 @Retention(RetentionPolicy.SOURCE)
377 public @interface SmartSuggestionMode {}
378
379 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800380 * {@code DeviceConfig} property used to set which Smart Suggestion modes for Augmented Autofill
381 * are available.
Felipe Leme7841d022019-02-07 13:02:18 -0800382 *
383 * @hide
384 */
385 @TestApi
386 public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
387 "smart_suggestion_supported_modes";
388
Felipe Lemea8209102019-02-21 18:01:47 -0800389 /**
390 * Sets how long (in ms) the augmented autofill service is bound while idle.
391 *
392 * <p>Use {@code 0} to keep it permanently bound.
393 *
394 * @hide
395 */
396 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
397 "augmented_service_idle_unbind_timeout";
398
399 /**
400 * Sets how long (in ms) the augmented autofill service request is killed if not replied.
401 *
402 * @hide
403 */
404 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
405 "augmented_service_request_timeout";
406
Felipe Leme80e7bf12019-02-14 10:56:52 -0800407 /** @hide */
408 public static final int RESULT_OK = 0;
409 /** @hide */
410 public static final int RESULT_CODE_NOT_SERVICE = -1;
411
Felipe Leme7841d022019-02-07 13:02:18 -0800412 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700413 * Makes an authentication id from a request id and a dataset id.
414 *
415 * @param requestId The request id.
416 * @param datasetId The dataset id.
417 * @return The authentication id.
418 * @hide
419 */
420 public static int makeAuthenticationId(int requestId, int datasetId) {
421 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
422 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
423 }
424
425 /**
426 * Gets the request id from an authentication id.
427 *
428 * @param authRequestId The authentication id.
429 * @return The request id.
430 * @hide
431 */
432 public static int getRequestIdFromAuthenticationId(int authRequestId) {
433 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
434 }
435
436 /**
437 * Gets the dataset id from an authentication id.
438 *
439 * @param authRequestId The authentication id.
440 * @return The dataset id.
441 * @hide
442 */
443 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
444 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
445 }
446
Felipe Leme4753bb02017-03-22 20:24:00 -0700447 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000448
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700449 /**
450 * There is currently no session running.
451 * {@hide}
452 */
Felipe Lemeadb34f52019-03-15 16:24:26 -0700453 public static final int NO_SESSION = Integer.MAX_VALUE;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700454
Svet Ganov782043c2017-02-11 00:52:02 +0000455 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700456
457 private final Object mLock = new Object();
458
459 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000460 private IAutoFillManagerClient mServiceClient;
461
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700462 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900463 private Cleaner mServiceClientCleaner;
464
465 @GuardedBy("mLock")
Felipe Leme284ad1c2018-11-15 18:16:12 -0800466 private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
467
468 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800469 private AutofillCallback mCallback;
470
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700471 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000472
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700473 @GuardedBy("mLock")
474 private int mSessionId = NO_SESSION;
475
476 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700477 private int mState = STATE_UNKNOWN;
478
479 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000480 private boolean mEnabled;
481
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700482 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700483 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700484 @Nullable private ParcelableMap mLastAutofilledData;
485
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700486 /** If view tracking is enabled, contains the tracking state */
487 @GuardedBy("mLock")
488 @Nullable private TrackedViews mTrackedViews;
489
Felipe Leme27e20222017-05-18 15:24:11 -0700490 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
491 @GuardedBy("mLock")
492 @Nullable private ArraySet<AutofillId> mFillableIds;
493
Dake Gub0fa3782018-02-26 12:25:14 -0800494 /** id of last requested autofill ui */
495 @Nullable private AutofillId mIdShownFillUi;
496
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800497 /**
498 * Views that were already "entered" - if they're entered again when the session is not active,
499 * they're ignored
500 * */
501 @GuardedBy("mLock")
502 @Nullable private ArraySet<AutofillId> mEnteredIds;
503
Felipe Lemea7de4022019-02-19 17:16:45 -0800504 /**
505 * Views that were otherwised not important for autofill but triggered a session because the
506 * context is whitelisted for augmented autofill.
507 */
508 @GuardedBy("mLock")
509 @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
510
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700511 /** If set, session is commited when the field is clicked. */
512 @GuardedBy("mLock")
513 @Nullable private AutofillId mSaveTriggerId;
514
Dake Gu67decfa2017-12-27 11:48:08 -0800515 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
516 @GuardedBy("mLock")
517 private boolean mOnInvisibleCalled;
518
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700519 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
520 @GuardedBy("mLock")
521 private boolean mSaveOnFinish;
522
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800523 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
524 @GuardedBy("mLock")
525 private CompatibilityBridge mCompatibilityBridge;
526
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800527 @Nullable
528 private final AutofillOptions mOptions;
529
Felipe Lemeadb34f52019-03-15 16:24:26 -0700530 /** When set, session is only used for augmented autofill requests. */
531 @GuardedBy("mLock")
532 private boolean mForAugmentedAutofillOnly;
533
Felipe Lemedf30f272019-03-19 13:47:48 -0700534 /**
Felipe Lemee5db59d2019-05-07 11:10:07 -0700535 * When set, standard autofill is disabled, but sessions can still be created for augmented
Felipe Lemedf30f272019-03-19 13:47:48 -0700536 * autofill only.
537 */
538 @GuardedBy("mLock")
539 private boolean mEnabledForAugmentedAutofillOnly;
540
Svet Ganov782043c2017-02-11 00:52:02 +0000541 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800542 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000543 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000544 * Asks the client to start an authentication flow.
545 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700546 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000547 * @param intent The authentication intent.
548 * @param fillInIntent The authentication fill-in intent.
549 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800550 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -0800551 Intent fillInIntent, boolean authenticateInline);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800552
553 /**
554 * Tells the client this manager has state to be reset.
555 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800556 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700557
558 /**
559 * Request showing the autofill UI.
560 *
561 * @param anchor The real view the UI needs to anchor to.
562 * @param width The width of the fill UI content.
563 * @param height The height of the fill UI content.
564 * @param virtualBounds The bounds of the virtual decendant of the anchor.
565 * @param presenter The presenter that controls the fill UI window.
566 * @return Whether the UI was shown.
567 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800568 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700569 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
570
571 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800572 * Dispatch unhandled keyevent from Autofill window
573 * @param anchor The real view the UI needs to anchor to.
574 * @param keyEvent Unhandled KeyEvent from autofill window.
575 */
576 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
577
578 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700579 * Request hiding the autofill UI.
580 *
581 * @return Whether the UI was hidden.
582 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800583 boolean autofillClientRequestHideFillUi();
584
585 /**
586 * Gets whether the fill UI is currenlty being shown.
587 *
588 * @return Whether the fill UI is currently being shown
589 */
590 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700591
592 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700593 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700594 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700595 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700596 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800597 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700598
599 /**
600 * Checks is the client is currently visible as understood by autofill.
601 *
602 * @return {@code true} if the client is currently visible
603 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800604 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700605
606 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800607 * Client might disable enter/exit event e.g. when activity is paused.
608 */
609 boolean isDisablingEnterExitEventForAutofill();
610
611 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700612 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700613 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800614 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700615 *
Felipe Leme27e20222017-05-18 15:24:11 -0700616 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700617 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800618 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
619 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700620
621 /**
622 * Finds a view by traversing the hierarchies of the client.
623 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800624 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700625 *
626 * @return The view, or {@code null} if not found
627 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800628 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
629
630 /**
631 * Finds a view by a11y id in a given client window.
632 *
633 * @param viewId The accessibility id of the views to find
634 * @param windowId The accessibility window id where to search
635 *
636 * @return The view, or {@code null} if not found
637 */
638 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700639
640 /**
641 * Runs the specified action on the UI thread.
642 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800643 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700644
645 /**
646 * Gets the complete component name of this client.
647 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800648 ComponentName autofillClientGetComponentName();
649
650 /**
651 * Gets the activity token
652 */
653 @Nullable IBinder autofillClientGetActivityToken();
654
655 /**
656 * @return Whether compatibility mode is enabled.
657 */
Felipe Leme42b97932018-02-20 13:04:31 -0800658 boolean autofillClientIsCompatibilityModeEnabled();
659
660 /**
661 * Gets the next unique autofill ID.
662 *
663 * <p>Typically used to manage views whose content is recycled - see
664 * {@link View#setAutofillId(AutofillId)} for more info.
665 *
666 * @return An ID that is unique in the activity.
667 */
668 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000669 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800670
671 /**
672 * @hide
673 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800674 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800675 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800676 mService = service;
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800677 mOptions = context.getAutofillOptions();
678
679 if (mOptions != null) {
680 sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
681 sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
682 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800683 }
684
685 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800686 * @hide
687 */
688 public void enableCompatibilityMode() {
689 synchronized (mLock) {
690 // The accessibility manager is a singleton so we may need to plug
691 // different bridge based on which activity is currently focused
692 // in the current process. Since compat would be rarely used, just
693 // create and register a new instance every time.
Felipe Lemece404982018-09-17 09:46:13 -0700694 if (sDebug) {
695 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
696 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800697 mCompatibilityBridge = new CompatibilityBridge();
698 }
699 }
700
701 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700702 * Restore state after activity lifecycle
703 *
704 * @param savedInstanceState The state to be restored
705 *
706 * {@hide}
707 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700708 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700709 if (!hasAutofillFeature()) {
710 return;
711 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700712 synchronized (mLock) {
713 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
714
Felipe Lemec24a56a2017-08-03 14:27:57 -0700715 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700716 Log.w(TAG, "New session was started before onCreate()");
717 return;
718 }
719
720 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700721 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700722
723 if (mSessionId != NO_SESSION) {
724 ensureServiceClientAddedIfNeededLocked();
725
Felipe Leme637e05e2017-12-06 12:09:37 -0800726 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700727 if (client != null) {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800728 final SyncResultReceiver receiver = new SyncResultReceiver(
729 SYNC_CALLS_TIMEOUT_MS);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700730 try {
Felipe Lemed4e52282018-06-18 13:56:38 -0700731 mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
732 mServiceClient.asBinder(), receiver);
733 final boolean sessionWasRestored = receiver.getIntResult() == 1;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700734
735 if (!sessionWasRestored) {
736 Log.w(TAG, "Session " + mSessionId + " could not be restored");
737 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700738 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700739 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700740 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700741 Log.d(TAG, "session " + mSessionId + " was restored");
742 }
743
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800744 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700745 }
746 } catch (RemoteException e) {
747 Log.e(TAG, "Could not figure out if there was an autofill session", e);
748 }
749 }
750 }
751 }
752 }
753
754 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700755 * Called once the client becomes visible.
756 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800757 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700758 *
759 * {@hide}
760 */
761 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800762 // This gets called when the client just got visible at which point the visibility
763 // of the tracked views may not have been computed (due to a pending layout, etc).
764 // While generally we have no way to know when the UI has settled. We will evaluate
765 // the tracked views state at the end of next frame to guarantee that everything
766 // that may need to be laid out is laid out.
767 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
768 synchronized (mLock) {
769 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
770 mTrackedViews.onVisibleForAutofillChangedLocked();
771 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700772 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800773 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700774 }
775
776 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800777 * Called once the client becomes invisible.
778 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800779 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800780 *
TYM Tsai88398352020-01-14 19:49:05 +0800781 * @param isExpiredResponse The response has expired or not
782 *
Dake Gu67decfa2017-12-27 11:48:08 -0800783 * {@hide}
784 */
TYM Tsai88398352020-01-14 19:49:05 +0800785 public void onInvisibleForAutofill(boolean isExpiredResponse) {
Dake Gu67decfa2017-12-27 11:48:08 -0800786 synchronized (mLock) {
787 mOnInvisibleCalled = true;
TYM Tsai88398352020-01-14 19:49:05 +0800788
789 if (isExpiredResponse) {
790 // Notify service the response has expired.
791 updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
792 ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
793 }
Dake Gu67decfa2017-12-27 11:48:08 -0800794 }
795 }
796
797 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700798 * Save state before activity lifecycle
799 *
800 * @param outState Place to store the state
801 *
802 * {@hide}
803 */
804 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700805 if (!hasAutofillFeature()) {
806 return;
807 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700808 synchronized (mLock) {
809 if (mSessionId != NO_SESSION) {
810 outState.putInt(SESSION_ID_TAG, mSessionId);
811 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700812 if (mState != STATE_UNKNOWN) {
813 outState.putInt(STATE_TAG, mState);
814 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700815 if (mLastAutofilledData != null) {
816 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
817 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700818 }
819 }
820
821 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800822 * @hide
823 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800824 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100825 public boolean isCompatibilityModeEnabledLocked() {
826 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800827 }
828
829 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700830 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700831 *
832 * <p>Typically used to determine whether the option to explicitly request autofill should
833 * be offered - see {@link #requestAutofill(View)}.
834 *
835 * @return whether autofill is enabled for the current user.
836 */
837 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800838 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700839 return false;
840 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700841 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800842 if (isDisabledByServiceLocked()) {
843 return false;
844 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700845 ensureServiceClientAddedIfNeededLocked();
846 return mEnabled;
847 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700848 }
849
850 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700851 * Should always be called from {@link AutofillService#getFillEventHistory()}.
852 *
853 * @hide
854 */
855 @Nullable public FillEventHistory getFillEventHistory() {
856 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800857 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -0700858 mService.getFillEventHistory(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -0800859 return receiver.getParcelableResult();
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700860 } catch (RemoteException e) {
861 e.rethrowFromSystemServer();
862 return null;
863 }
864 }
865
866 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700867 * Explicitly requests a new autofill context.
868 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700869 * <p>Normally, the autofill context is automatically started if necessary when
870 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
871 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
872 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700873 *
874 * @param view view requesting the new autofill context.
875 */
876 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700877 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700878 }
879
880 /**
881 * Explicitly requests a new autofill context for virtual views.
882 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700883 * <p>Normally, the autofill context is automatically started if necessary when
884 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
885 * cases where it must be explicitly started. For example, when the virtual view offers an
886 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700887 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700888 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
889 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
890 * the absolute bounds could be calculated by:
891 *
892 * <pre class="prettyprint">
893 * int offset[] = new int[2];
894 * getLocationOnScreen(offset);
895 * Rect absBounds = new Rect(bounds.left + offset[0],
896 * bounds.top + offset[1],
897 * bounds.right + offset[0], bounds.bottom + offset[1]);
898 * </pre>
899 *
900 * @param view the virtual view parent.
901 * @param virtualId id identifying the virtual child inside the parent view.
902 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700903 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700904 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
905 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700906 }
907
Felipe Leme2ac463e2017-03-13 14:06:25 -0700908 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700909 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800910 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700911 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800912 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700913 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700914 notifyViewEntered(view, 0);
915 }
916
Andreas Gampe3f24e692018-02-05 13:24:28 -0800917 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800918 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800919 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700920 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800921 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
922 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700923 }
924 return true;
925 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800926 if (isFinishedLocked()) {
927 // Session already finished: ignore if automatic request and view already entered
928 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
929 && mEnteredIds.contains(id)) {
930 if (sVerbose) {
931 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
932 + ") on state " + getStateAsStringLocked()
933 + " because view was already entered: " + mEnteredIds);
934 }
935 return true;
936 }
937 }
Felipe Leme17292d12017-10-24 14:03:10 -0700938 return false;
939 }
940
Dake Gu67decfa2017-12-27 11:48:08 -0800941 private boolean isClientVisibleForAutofillLocked() {
942 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800943 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800944 }
945
946 private boolean isClientDisablingEnterExitEvent() {
947 final AutofillClient client = getClient();
948 return client != null && client.isDisablingEnterExitEventForAutofill();
949 }
950
Felipe Lemed1146422017-04-26 10:17:05 -0700951 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700952 if (!hasAutofillFeature()) {
953 return;
954 }
Dake Gu67decfa2017-12-27 11:48:08 -0800955 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700956 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800957 callback = notifyViewEnteredLocked(view, flags);
958 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700959
Dake Gu67decfa2017-12-27 11:48:08 -0800960 if (callback != null) {
961 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
962 }
963 }
Svet Ganov782043c2017-02-11 00:52:02 +0000964
Dake Gu67decfa2017-12-27 11:48:08 -0800965 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800966 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800967 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800968 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800969 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800970
971 AutofillCallback callback = null;
972
973 ensureServiceClientAddedIfNeededLocked();
974
Felipe Lemedf30f272019-03-19 13:47:48 -0700975 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemeadb34f52019-03-15 16:24:26 -0700976 if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
977
Dake Gu67decfa2017-12-27 11:48:08 -0800978 if (mCallback != null) {
979 callback = mCallback;
980 }
981 } else {
982 // don't notify entered when Activity is already in background
983 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700984 final AutofillValue value = view.getAutofillValue();
985
Felipe Lemec24a56a2017-08-03 14:27:57 -0700986 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700987 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700988 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700989 } else {
990 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -0700991 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
992 if (sDebug) {
993 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
994 + "mForAugmentedAutofillOnly on manual request");
995 }
996 mForAugmentedAutofillOnly = false;
997 }
Felipe Leme0aa4c502017-04-26 12:36:01 -0700998 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700999 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001000 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001001 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001002 }
Dake Gu67decfa2017-12-27 11:48:08 -08001003 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -08001004 }
1005
1006 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001007 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001008 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001009 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001010 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001011 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001012 if (!hasAutofillFeature()) {
1013 return;
1014 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001015 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001016 notifyViewExitedLocked(view);
1017 }
1018 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001019
Andreas Gampe3f24e692018-02-05 13:24:28 -08001020 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001021 void notifyViewExitedLocked(@NonNull View view) {
1022 ensureServiceClientAddedIfNeededLocked();
1023
Felipe Lemedf30f272019-03-19 13:47:48 -07001024 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001025 // dont notify exited when Activity is already in background
1026 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -08001027 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -08001028
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001029 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001030 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001031 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001032 }
1033 }
1034
1035 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001036 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001037 *
1038 * @param view {@link View} that was exited.
1039 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001040 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001041 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1042 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1043 }
1044
1045 /**
1046 * Called when a virtual view's visibility changed.
1047 *
1048 * @param view {@link View} that was exited.
1049 * @param virtualId id identifying the virtual child inside the parent view.
1050 * @param isVisible visible if the view is visible in the view hierarchy.
1051 */
1052 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1053 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1054 }
1055
1056 /**
1057 * Called when a view/virtual view's visibility changed.
1058 *
1059 * @param view {@link View} that was exited.
1060 * @param virtualId id identifying the virtual child inside the parent view.
1061 * @param isVisible visible if the view is visible in the view hierarchy.
1062 * @param virtual Whether the view is virtual.
1063 */
1064 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1065 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001066 synchronized (mLock) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001067 if (mForAugmentedAutofillOnly) {
1068 if (sVerbose) {
1069 Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
1070 }
1071 return;
1072 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001073 if (mEnabled && isActiveLocked()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001074 final AutofillId id = virtual ? getAutofillId(view, virtualId)
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001075 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -08001076 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -07001077 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001078 if (mFillableIds.contains(id)) {
1079 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1080 requestHideFillUi(id, view);
1081 }
1082 }
1083 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001084 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -08001085 } else if (sVerbose) {
1086 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -07001087 }
lpetere28d6b02019-11-27 15:02:42 +08001088 } else if (!virtual && isVisible) {
1089 startAutofillIfNeededLocked(view);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001090 }
1091 }
1092 }
1093
1094 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001095 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001096 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001097 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1098 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1099 * the absolute bounds could be calculated by:
1100 *
1101 * <pre class="prettyprint">
1102 * int offset[] = new int[2];
1103 * getLocationOnScreen(offset);
1104 * Rect absBounds = new Rect(bounds.left + offset[0],
1105 * bounds.top + offset[1],
1106 * bounds.right + offset[0], bounds.bottom + offset[1]);
1107 * </pre>
1108 *
1109 * @param view the virtual view parent.
1110 * @param virtualId id identifying the virtual child inside the parent view.
1111 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -08001112 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001113 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1114 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -07001115 }
1116
Felipe Leme6dcec872017-05-25 11:24:23 -07001117 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -07001118 if (!hasAutofillFeature()) {
1119 return;
1120 }
Dake Gu67decfa2017-12-27 11:48:08 -08001121 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001122 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001123 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
1124 }
Felipe Leme17292d12017-10-24 14:03:10 -07001125
Dake Gu67decfa2017-12-27 11:48:08 -08001126 if (callback != null) {
1127 callback.onAutofillEvent(view, virtualId,
1128 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1129 }
1130 }
Svet Ganov782043c2017-02-11 00:52:02 +00001131
Dake Gu67decfa2017-12-27 11:48:08 -08001132 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001133 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001134 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
1135 int flags) {
Felipe Leme305fd402018-09-11 18:20:42 +00001136 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -08001137 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001138 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -08001139
1140 ensureServiceClientAddedIfNeededLocked();
1141
Felipe Lemedf30f272019-03-19 13:47:48 -07001142 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemef3b844b2018-10-02 13:50:50 -07001143 if (sVerbose) {
1144 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1145 }
Dake Gu67decfa2017-12-27 11:48:08 -08001146 if (mCallback != null) {
1147 callback = mCallback;
1148 }
1149 } else {
1150 // don't notify entered when Activity is already in background
1151 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001152 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001153 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001154 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001155 } else {
1156 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -07001157 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1158 if (sDebug) {
1159 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1160 + "mForAugmentedAutofillOnly on manual request");
1161 }
1162 mForAugmentedAutofillOnly = false;
1163 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001164 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001165 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001166 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001167 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001168 }
Dake Gu67decfa2017-12-27 11:48:08 -08001169 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -08001170 }
1171
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001172 @GuardedBy("mLock")
1173 private void addEnteredIdLocked(@NonNull AutofillId id) {
1174 if (mEnteredIds == null) {
1175 mEnteredIds = new ArraySet<>(1);
1176 }
Felipe Leme785777b2019-04-30 18:12:50 -07001177 id.resetSessionId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001178 mEnteredIds.add(id);
1179 }
1180
Philip P. Moltmann44611812017-02-23 12:52:46 -08001181 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001182 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001183 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001184 * @param view the virtual view parent.
1185 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001186 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001187 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001188 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001189 if (!hasAutofillFeature()) {
1190 return;
1191 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001192 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001193 notifyViewExitedLocked(view, virtualId);
1194 }
1195 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001196
Andreas Gampe3f24e692018-02-05 13:24:28 -08001197 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001198 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1199 ensureServiceClientAddedIfNeededLocked();
1200
Felipe Lemedf30f272019-03-19 13:47:48 -07001201 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001202 // don't notify exited when Activity is already in background
1203 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001204 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001205
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001206 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001207 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001208 }
Svet Ganov782043c2017-02-11 00:52:02 +00001209 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001210 }
1211
1212 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001213 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001214 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001215 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001216 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001217 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001218 if (!hasAutofillFeature()) {
1219 return;
1220 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001221 AutofillId id = null;
1222 boolean valueWasRead = false;
1223 AutofillValue value = null;
1224
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001225 synchronized (mLock) {
1226 // If the session is gone some fields might still be highlighted, hence we have to
1227 // remove the isAutofilled property even if no sessions are active.
1228 if (mLastAutofilledData == null) {
1229 view.setAutofilled(false);
1230 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001231 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001232 if (mLastAutofilledData.containsKey(id)) {
1233 value = view.getAutofillValue();
1234 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001235
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001236 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1237 view.setAutofilled(true);
1238 } else {
1239 view.setAutofilled(false);
1240 mLastAutofilledData.remove(id);
1241 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001242 } else {
1243 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001244 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001245 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001246
Felipe Lemedbb7a362019-05-13 15:28:46 -07001247 if (mForAugmentedAutofillOnly) {
1248 if (sVerbose) {
1249 Log.v(TAG, "notifyValueChanged(): not notifying system server on "
1250 + "augmented-only mode");
1251 }
1252 return;
1253 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001254 if (!mEnabled || !isActiveLocked()) {
lpetere28d6b02019-11-27 15:02:42 +08001255 if (!startAutofillIfNeededLocked(view)) {
1256 if (sVerbose) {
1257 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1258 + "): ignoring on state " + getStateAsStringLocked());
1259 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001260 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001261 return;
1262 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001263
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001264 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001265 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001266 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001267
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001268 if (!valueWasRead) {
1269 value = view.getAutofillValue();
1270 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001271
Felipe Leme0aa4c502017-04-26 12:36:01 -07001272 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001273 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001274 }
1275
Felipe Lemebab851c2017-02-03 18:45:08 -08001276 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001277 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001278 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001279 * @param view the virtual view parent.
1280 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001281 * @param value new value of the child.
1282 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001283 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001284 if (!hasAutofillFeature()) {
1285 return;
1286 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001287 synchronized (mLock) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001288 if (mForAugmentedAutofillOnly) {
1289 if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
1290 return;
1291 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001292 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001293 if (sVerbose) {
1294 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1295 + "): ignoring on state " + getStateAsStringLocked());
1296 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001297 return;
1298 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001299
Felipe Leme305fd402018-09-11 18:20:42 +00001300 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001301 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001302 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001303 }
1304
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001305 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001306 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001307 *
Felipe Leme67e62092018-02-15 14:47:31 -08001308 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001309 */
Felipe Leme67e62092018-02-15 14:47:31 -08001310 public void notifyViewClicked(@NonNull View view) {
1311 notifyViewClicked(view.getAutofillId());
1312 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001313
Felipe Leme67e62092018-02-15 14:47:31 -08001314 /**
1315 * Called to indicate a virtual view has been clicked.
1316 *
1317 * @param view the virtual view parent.
1318 * @param virtualId id identifying the virtual child inside the parent view.
1319 */
1320 public void notifyViewClicked(@NonNull View view, int virtualId) {
Felipe Leme305fd402018-09-11 18:20:42 +00001321 notifyViewClicked(getAutofillId(view, virtualId));
Felipe Leme67e62092018-02-15 14:47:31 -08001322 }
1323
1324 private void notifyViewClicked(AutofillId id) {
1325 if (!hasAutofillFeature()) {
1326 return;
1327 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001328 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1329
1330 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001331 if (!mEnabled || !isActiveLocked()) {
1332 return;
1333 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001334 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1335 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1336 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001337 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001338 }
1339 }
1340 }
1341
1342 /**
1343 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1344 *
1345 * @hide
1346 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001347 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001348 if (!hasAutofillFeature()) {
1349 return;
1350 }
1351 synchronized (mLock) {
1352 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001353 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001354 commitLocked();
1355 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001356 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001357 cancelLocked();
1358 }
1359 }
1360 }
1361
Felipe Lemebab851c2017-02-03 18:45:08 -08001362 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001363 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001364 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001365 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1366 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1367 * that represent the HTML elements, it should call this method after the form is submitted and
1368 * another page is rendered.
1369 *
1370 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1371 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001372 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001373 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001374 if (!hasAutofillFeature()) {
1375 return;
1376 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001377 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001378 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001379 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001380 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001381 }
1382
Andreas Gampe3f24e692018-02-05 13:24:28 -08001383 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001384 private void commitLocked() {
1385 if (!mEnabled && !isActiveLocked()) {
1386 return;
1387 }
1388 finishSessionLocked();
1389 }
1390
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001391 /**
1392 * Called to indicate the current autofill context should be cancelled.
1393 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001394 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1395 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1396 * that represent the HTML elements, it should call this method if the user does not post the
1397 * form but moves to another form in this page.
1398 *
1399 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1400 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001401 */
1402 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001403 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001404 if (!hasAutofillFeature()) {
1405 return;
1406 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001407 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001408 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001409 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001410 }
1411
Andreas Gampe3f24e692018-02-05 13:24:28 -08001412 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001413 private void cancelLocked() {
1414 if (!mEnabled && !isActiveLocked()) {
1415 return;
1416 }
1417 cancelSessionLocked();
1418 }
1419
Svet Ganovf965b872017-04-28 16:34:02 -07001420 /** @hide */
1421 public void disableOwnedAutofillServices() {
1422 disableAutofillServices();
1423 }
1424
Svet Ganovf20a0372017-04-10 17:08:05 -07001425 /**
1426 * If the app calling this API has enabled autofill services they
1427 * will be disabled.
1428 */
Svet Ganovf965b872017-04-28 16:34:02 -07001429 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001430 if (!hasAutofillFeature()) {
1431 return;
1432 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001433 try {
1434 mService.disableOwnedAutofillServices(mContext.getUserId());
1435 } catch (RemoteException e) {
1436 throw e.rethrowFromSystemServer();
1437 }
1438 }
1439
Felipe Lemedb041182017-04-21 17:33:38 -07001440 /**
1441 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1442 * enabled for the current user, or {@code false} otherwise.
1443 */
1444 public boolean hasEnabledAutofillServices() {
1445 if (mService == null) return false;
1446
Felipe Lemec0c15a32019-01-08 10:53:33 -08001447 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001448 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001449 mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
1450 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001451 } catch (RemoteException e) {
1452 throw e.rethrowFromSystemServer();
1453 }
1454 }
1455
1456 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001457 * Returns the component name of the {@link AutofillService} that is enabled for the current
1458 * user.
1459 */
1460 @Nullable
1461 public ComponentName getAutofillServiceComponentName() {
1462 if (mService == null) return null;
1463
Felipe Lemec0c15a32019-01-08 10:53:33 -08001464 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme23c75ff2017-12-14 13:27:44 -08001465 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001466 mService.getAutofillServiceComponentName(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001467 return receiver.getParcelableResult();
Felipe Leme23c75ff2017-12-14 13:27:44 -08001468 } catch (RemoteException e) {
1469 throw e.rethrowFromSystemServer();
1470 }
1471 }
1472
1473 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001474 * Gets the id of the {@link UserData} used for
1475 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1476 *
1477 * <p>This method is useful when the service must check the status of the {@link UserData} in
1478 * the device without fetching the whole object.
1479 *
1480 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1481 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1482 * the user.
1483 *
1484 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1485 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1486 * service for the user.
1487 */
1488 @Nullable public String getUserDataId() {
1489 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001490 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001491 mService.getUserDataId(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001492 return receiver.getStringResult();
Felipe Lemef0baef742018-01-26 14:39:39 -08001493 } catch (RemoteException e) {
1494 e.rethrowFromSystemServer();
1495 return null;
1496 }
1497 }
1498
1499 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001500 * Gets the user data used for
1501 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001502 *
Felipe Leme27f45732017-12-22 09:05:22 -08001503 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1504 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1505 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001506 *
Felipe Leme452886a2017-11-27 13:09:13 -08001507 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1508 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001509 */
Felipe Leme452886a2017-11-27 13:09:13 -08001510 @Nullable public UserData getUserData() {
1511 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001512 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001513 mService.getUserData(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001514 return receiver.getParcelableResult();
Felipe Leme452886a2017-11-27 13:09:13 -08001515 } catch (RemoteException e) {
1516 e.rethrowFromSystemServer();
1517 return null;
1518 }
1519 }
1520
1521 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001522 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001523 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001524 *
1525 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1526 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1527 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001528 */
Felipe Leme452886a2017-11-27 13:09:13 -08001529 public void setUserData(@Nullable UserData userData) {
1530 try {
1531 mService.setUserData(userData);
1532 } catch (RemoteException e) {
1533 e.rethrowFromSystemServer();
1534 }
1535 }
1536
1537 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001538 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1539 * enabled.
1540 *
1541 * <p>As field classification is an expensive operation, it could be disabled, either
1542 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1543 * permanently (for example, because the device is a low-level device).
1544 *
1545 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1546 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1547 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001548 */
Felipe Leme329d0402017-12-06 09:22:43 -08001549 public boolean isFieldClassificationEnabled() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001550 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme329d0402017-12-06 09:22:43 -08001551 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001552 mService.isFieldClassificationEnabled(receiver);
1553 return receiver.getIntResult() == 1;
Felipe Leme329d0402017-12-06 09:22:43 -08001554 } catch (RemoteException e) {
1555 e.rethrowFromSystemServer();
1556 return false;
1557 }
1558 }
1559
1560 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001561 * Gets the name of the default algorithm used for
1562 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1563 *
1564 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1565 * set.
1566 *
1567 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1568 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1569 * the user.
1570 */
1571 @Nullable
1572 public String getDefaultFieldClassificationAlgorithm() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001573 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001574 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001575 mService.getDefaultFieldClassificationAlgorithm(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001576 return receiver.getStringResult();
Felipe Leme27f45732017-12-22 09:05:22 -08001577 } catch (RemoteException e) {
1578 e.rethrowFromSystemServer();
1579 return null;
1580 }
1581 }
1582
1583 /**
1584 * Gets the name of all algorithms currently available for
1585 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1586 *
1587 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001588 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1589 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001590 */
1591 @NonNull
1592 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001593 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001594 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001595 mService.getAvailableFieldClassificationAlgorithms(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001596 final String[] algorithms = receiver.getStringArrayResult();
Felipe Lemee4ac7402018-01-16 19:37:00 -08001597 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001598 } catch (RemoteException e) {
1599 e.rethrowFromSystemServer();
1600 return null;
1601 }
1602 }
1603
1604 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001605 * Returns {@code true} if autofill is supported by the current device and
1606 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001607 *
1608 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1609 * <ol>
1610 * <li>Low-end devices.
1611 * <li>Device policy rules that forbid its usage.
1612 * </ol>
1613 */
1614 public boolean isAutofillSupported() {
1615 if (mService == null) return false;
1616
Felipe Lemec0c15a32019-01-08 10:53:33 -08001617 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001618 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001619 mService.isServiceSupported(mContext.getUserId(), receiver);
1620 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001621 } catch (RemoteException e) {
1622 throw e.rethrowFromSystemServer();
1623 }
1624 }
1625
Felipe Leme637e05e2017-12-06 12:09:37 -08001626 // Note: don't need to use locked suffix because mContext is final.
1627 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001628 final AutofillClient client = mContext.getAutofillClient();
Felipe Leme261e75f2018-12-17 10:15:08 -08001629 if (client == null && sVerbose) {
1630 Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
Felipe Leme686128e2017-10-17 14:02:20 -07001631 + mContext);
1632 }
1633 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001634 }
1635
Dake Gud9dbd272018-03-13 11:38:42 -07001636 /**
1637 * Check if autofill ui is showing, must be called on UI thread.
1638 * @hide
1639 */
1640 public boolean isAutofillUiShowing() {
1641 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001642 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001643 }
1644
Svet Ganov782043c2017-02-11 00:52:02 +00001645 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001646 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001647 if (!hasAutofillFeature()) {
1648 return;
1649 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001650 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001651 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1652 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1653 // service set the extra and returned RESULT_CANCELED...
1654
Felipe Leme1ad95f02018-06-15 13:07:34 -07001655 if (sDebug) {
1656 Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
1657 }
Felipe Lemed633f072017-02-14 10:17:17 -08001658
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001659 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001660 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001661 return;
1662 }
Dake Gu67decfa2017-12-27 11:48:08 -08001663 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1664 // of app activity. We enforce enter event to re-show fill ui in such case.
1665 // CTS example:
1666 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1667 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1668 if (!mOnInvisibleCalled && focusView != null
1669 && focusView.canNotifyAutofillEnterExitEvent()) {
1670 notifyViewExitedLocked(focusView);
1671 notifyViewEnteredLocked(focusView, 0);
1672 }
1673 if (data == null) {
1674 // data is set to null when result is not RESULT_OK
1675 return;
1676 }
1677
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001678 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1679 final Bundle responseData = new Bundle();
1680 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001681 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1682 if (newClientState != null) {
1683 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1684 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001685 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001686 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1687 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001688 } catch (RemoteException e) {
1689 Log.e(TAG, "Error delivering authentication result", e);
1690 }
Svet Ganov782043c2017-02-11 00:52:02 +00001691 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001692 }
1693
Felipe Leme42b97932018-02-20 13:04:31 -08001694 /**
1695 * Gets the next unique autofill ID for the activity context.
1696 *
1697 * <p>Typically used to manage views whose content is recycled - see
1698 * {@link View#setAutofillId(AutofillId)} for more info.
1699 *
1700 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1701 * the {@link Context} associated with this {@link AutofillManager}.
1702 */
1703 @Nullable
1704 public AutofillId getNextAutofillId() {
1705 final AutofillClient client = getClient();
1706 if (client == null) return null;
1707
1708 final AutofillId id = client.autofillClientGetNextAutofillId();
1709
1710 if (id == null && sDebug) {
1711 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1712 }
1713
1714 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001715 }
1716
Felipe Leme305fd402018-09-11 18:20:42 +00001717 private static AutofillId getAutofillId(View parent, int virtualId) {
1718 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001719 }
1720
Andreas Gampe3f24e692018-02-05 13:24:28 -08001721 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001722 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1723 @NonNull AutofillValue value, int flags) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001724 if (mEnteredForAugmentedAutofillIds != null
Felipe Lemedf30f272019-03-19 13:47:48 -07001725 && mEnteredForAugmentedAutofillIds.contains(id)
1726 || mEnabledForAugmentedAutofillOnly) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001727 if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
Felipe Lemedf30f272019-03-19 13:47:48 -07001728 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
Felipe Lemea7de4022019-02-19 17:16:45 -08001729 }
Felipe Leme9f9ee252017-04-27 13:56:22 -07001730 if (sVerbose) {
1731 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001732 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001733 + ", compatMode=" + isCompatibilityModeEnabledLocked()
Felipe Lemedf30f272019-03-19 13:47:48 -07001734 + ", augmentedOnly=" + mForAugmentedAutofillOnly
1735 + ", enabledAugmentedOnly=" + mEnabledForAugmentedAutofillOnly
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001736 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001737 }
Felipe Lemee5db59d2019-05-07 11:10:07 -07001738 // We need to reset the augmented-only state when a manual request is made, as it's possible
1739 // that the service returned null for the first request and now the user is manually
1740 // requesting autofill to trigger a custom UI provided by the service.
1741 if (mForAugmentedAutofillOnly && !mEnabledForAugmentedAutofillOnly
1742 && (flags & FLAG_MANUAL_REQUEST) != 0) {
1743 if (sVerbose) {
1744 Log.v(TAG, "resetting mForAugmentedAutofillOnly on manual autofill request");
1745 }
1746 mForAugmentedAutofillOnly = false;
1747 }
Felipe Leme3103c632017-12-18 15:05:14 -08001748 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001749 if (sVerbose) {
1750 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001751 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001752 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001753 return;
1754 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001755 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001756 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001757 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001758
Felipe Lemec0c15a32019-01-08 10:53:33 -08001759 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemeadb34f52019-03-15 16:24:26 -07001760 final ComponentName componentName = client.autofillClientGetComponentName();
Joanne Chung9e247b12019-07-19 23:41:32 +08001761
1762 if (!mEnabledForAugmentedAutofillOnly && mOptions != null
1763 && mOptions.isAutofillDisabledLocked(componentName)) {
1764 if (mOptions.isAugmentedAutofillEnabled(mContext)) {
1765 if (sDebug) {
1766 Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
1767 + "whitelisted for augmented autofill");
1768 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
1769 }
1770 } else {
1771 if (sDebug) {
1772 Log.d(TAG, "startSession(" + componentName + "): ignored because "
1773 + "disabled by service and not whitelisted for augmented autofill");
1774 }
1775 setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
1776 client.autofillClientResetableStateAvailable();
1777 return;
1778 }
1779 }
1780
Felipe Lemed4e52282018-06-18 13:56:38 -07001781 mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001782 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Lemeadb34f52019-03-15 16:24:26 -07001783 mCallback != null, flags, componentName,
Felipe Lemed4e52282018-06-18 13:56:38 -07001784 isCompatibilityModeEnabledLocked(), receiver);
1785 mSessionId = receiver.getIntResult();
Felipe Lemec24a56a2017-08-03 14:27:57 -07001786 if (mSessionId != NO_SESSION) {
1787 mState = STATE_ACTIVE;
1788 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07001789 final int extraFlags = receiver.getOptionalExtraIntResult(0);
Felipe Lemee5db59d2019-05-07 11:10:07 -07001790 if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001791 if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
1792 mForAugmentedAutofillOnly = true;
1793 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001794 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001795 } catch (RemoteException e) {
1796 throw e.rethrowFromSystemServer();
1797 }
1798 }
1799
Andreas Gampe3f24e692018-02-05 13:24:28 -08001800 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001801 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001802 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001803
1804 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001805
Svet Ganov782043c2017-02-11 00:52:02 +00001806 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001807 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001808 } catch (RemoteException e) {
1809 throw e.rethrowFromSystemServer();
1810 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001811
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001812 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001813 }
1814
Andreas Gampe3f24e692018-02-05 13:24:28 -08001815 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001816 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001817 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001818
1819 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001820
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001821 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001822 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001823 } catch (RemoteException e) {
1824 throw e.rethrowFromSystemServer();
1825 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001826
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001827 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001828 }
1829
Andreas Gampe3f24e692018-02-05 13:24:28 -08001830 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001831 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001832 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001833 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001834 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001835 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001836 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001837 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001838 if (resetEnteredIds) {
1839 mEnteredIds = null;
1840 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001841 }
1842
Andreas Gampe3f24e692018-02-05 13:24:28 -08001843 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001844 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1845 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001846 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001847 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1848 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001849 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001850 try {
Felipe Lemed62b5162018-06-06 15:08:25 -07001851 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1852 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -08001853 } catch (RemoteException e) {
1854 throw e.rethrowFromSystemServer();
1855 }
1856 }
Svet Ganov782043c2017-02-11 00:52:02 +00001857
Andreas Gampe3f24e692018-02-05 13:24:28 -08001858 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001859 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Lemedf30f272019-03-19 13:47:48 -07001860 final AutofillClient client = getClient();
1861 if (client == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001862 return;
1863 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001864
Svet Ganov782043c2017-02-11 00:52:02 +00001865 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001866 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001867 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001868 final int userId = mContext.getUserId();
Felipe Lemec0c15a32019-01-08 10:53:33 -08001869 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedf30f272019-03-19 13:47:48 -07001870 mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
1871 userId, receiver);
Felipe Lemed4e52282018-06-18 13:56:38 -07001872 final int flags = receiver.getIntResult();
Felipe Leme9f9ee252017-04-27 13:56:22 -07001873 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1874 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1875 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Felipe Lemedf30f272019-03-19 13:47:48 -07001876 mEnabledForAugmentedAutofillOnly = (flags
1877 & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
1878 if (sVerbose) {
1879 Log.v(TAG, "receiver results: flags=" + flags + " enabled=" + mEnabled
1880 + ", enabledForAugmentedOnly: " + mEnabledForAugmentedAutofillOnly);
1881 }
Koji Fukuiccec6a62017-10-18 17:48:40 +09001882 final IAutoFillManager service = mService;
1883 final IAutoFillManagerClient serviceClient = mServiceClient;
1884 mServiceClientCleaner = Cleaner.create(this, () -> {
Felipe Leme559e21d2019-01-18 17:57:21 -08001885 // TODO(b/123100811): call service to also remove reference to
Felipe Leme284ad1c2018-11-15 18:16:12 -08001886 // mAugmentedAutofillServiceClient
Koji Fukuiccec6a62017-10-18 17:48:40 +09001887 try {
1888 service.removeClient(serviceClient, userId);
1889 } catch (RemoteException e) {
1890 }
1891 });
Svet Ganov782043c2017-02-11 00:52:02 +00001892 } catch (RemoteException e) {
1893 throw e.rethrowFromSystemServer();
1894 }
1895 }
1896 }
1897
lpetere28d6b02019-11-27 15:02:42 +08001898 @GuardedBy("mLock")
1899 private boolean startAutofillIfNeededLocked(View view) {
1900 if (mState == STATE_UNKNOWN
1901 && mSessionId == NO_SESSION
1902 && view instanceof EditText
1903 && !TextUtils.isEmpty(((EditText) view).getText())
1904 && !view.isFocused()
1905 && view.isImportantForAutofill()
1906 && view.isLaidOut()
1907 && view.isVisibleToUser()) {
1908
1909 ensureServiceClientAddedIfNeededLocked();
1910
1911 if (sVerbose) {
1912 Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
1913 }
1914 if (mEnabled && !isClientDisablingEnterExitEvent()) {
1915 final AutofillId id = view.getAutofillId();
1916 final AutofillValue value = view.getAutofillValue();
1917 // Starts new session.
1918 startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
1919 // Updates value.
1920 updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
1921 /* flags= */ 0);
1922 addEnteredIdLocked(id);
1923 return true;
1924 }
1925 }
1926 return false;
1927 }
1928
Felipe Lemee6010f22017-03-03 11:19:51 -08001929 /**
1930 * Registers a {@link AutofillCallback} to receive autofill events.
1931 *
1932 * @param callback callback to receive events.
1933 */
1934 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001935 if (!hasAutofillFeature()) {
1936 return;
1937 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001938 synchronized (mLock) {
1939 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001940
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001941 final boolean hadCallback = mCallback != null;
1942 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001943
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001944 if (!hadCallback) {
1945 try {
1946 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1947 } catch (RemoteException e) {
1948 throw e.rethrowFromSystemServer();
1949 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001950 }
1951 }
1952 }
1953
1954 /**
1955 * Unregisters a {@link AutofillCallback} to receive autofill events.
1956 *
1957 * @param callback callback to stop receiving events.
1958 */
1959 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001960 if (!hasAutofillFeature()) {
1961 return;
1962 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001963 synchronized (mLock) {
1964 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001965
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001966 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001967
Felipe Lemee6010f22017-03-03 11:19:51 -08001968 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001969 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001970 } catch (RemoteException e) {
1971 throw e.rethrowFromSystemServer();
1972 }
1973 }
1974 }
1975
Felipe Leme559e21d2019-01-18 17:57:21 -08001976 /**
Felipe Leme559e21d2019-01-18 17:57:21 -08001977 * Explicitly limits augmented autofill to the given packages and activities.
1978 *
Felipe Leme559e21d2019-01-18 17:57:21 -08001979 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
1980 *
1981 * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
1982 * apps that uses addresses. For example, if the service wants to support augmented autofill on
1983 * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
1984 * of {@code AddressApp2}, it would call:
1985 * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
1986 * Arrays.asList(new ComponentName("AddressApp2", "act1"),
1987 * new ComponentName("AddressApp2", "act2")));}
1988 *
1989 * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
1990 * service, and it's ignored if the caller isn't it.
1991 *
1992 * @hide
1993 */
1994 @SystemApi
1995 @TestApi
Felipe Leme7a3c9f52019-02-13 16:32:49 -08001996 public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
1997 @Nullable Set<ComponentName> activities) {
Felipe Leme80e7bf12019-02-14 10:56:52 -08001998 if (!hasAutofillFeature()) {
1999 return;
2000 }
2001
2002 final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2003 final int resultCode;
2004 try {
2005 mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
2006 resultReceiver);
2007 resultCode = resultReceiver.getIntResult();
2008 } catch (RemoteException e) {
2009 throw e.rethrowFromSystemServer();
2010 }
2011 switch (resultCode) {
2012 case RESULT_OK:
2013 return;
2014 case RESULT_CODE_NOT_SERVICE:
2015 throw new SecurityException("caller is not user's Augmented Autofill Service");
2016 default:
2017 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
2018 + resultCode);
2019 }
2020 }
2021
Felipe Lemea7de4022019-02-19 17:16:45 -08002022 /**
2023 * Notifies that a non-autofillable view was entered because the activity is whitelisted for
2024 * augmented autofill.
2025 *
2026 * <p>This method is necessary to set the right flag on start, so the server-side session
2027 * doesn't trigger the standard autofill workflow, but the augmented's instead.
2028 *
2029 * @hide
2030 */
2031 public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
2032 final AutofillId id = view.getAutofillId();
2033 synchronized (mLock) {
2034 if (mEnteredForAugmentedAutofillIds == null) {
2035 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
2036 }
2037 mEnteredForAugmentedAutofillIds.add(id);
2038 }
2039 }
2040
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002041 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2042 Rect anchorBounds, IAutofillWindowPresenter presenter) {
2043 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07002044 if (anchor == null) {
2045 return;
2046 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002047
2048 AutofillCallback callback = null;
2049 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002050 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002051 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002052
2053 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002054 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08002055 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002056 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08002057 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002058 }
2059 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002060 }
2061 }
2062
2063 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002064 if (id.isVirtualInt()) {
2065 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002066 AutofillCallback.EVENT_INPUT_SHOWN);
2067 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002068 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002069 }
2070 }
2071 }
2072
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002073 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08002074 Intent fillInIntent, boolean authenticateInline) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002075 synchronized (mLock) {
2076 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002077 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002078 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08002079 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
2080 // before onAuthenticationResult()
2081 mOnInvisibleCalled = false;
Adam He5be0f152020-02-03 15:53:32 -08002082 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
2083 authenticateInline);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002084 }
2085 }
2086 }
2087 }
2088
Dake Gu6a20a192018-02-08 12:09:30 -08002089 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
2090 final View anchor = findView(id);
2091 if (anchor == null) {
2092 return;
2093 }
2094
Dake Gu6a20a192018-02-08 12:09:30 -08002095 synchronized (mLock) {
2096 if (mSessionId == sessionId) {
2097 AutofillClient client = getClient();
2098
2099 if (client != null) {
2100 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
2101 }
2102 }
2103 }
2104 }
2105
Felipe Leme51e29da2017-10-24 14:03:10 -07002106 /** @hide */
2107 public static final int SET_STATE_FLAG_ENABLED = 0x01;
2108 /** @hide */
2109 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
2110 /** @hide */
2111 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
2112 /** @hide */
2113 public static final int SET_STATE_FLAG_DEBUG = 0x08;
2114 /** @hide */
2115 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
Felipe Lemee5db59d2019-05-07 11:10:07 -07002116 /** @hide */
2117 public static final int SET_STATE_FLAG_FOR_AUTOFILL_ONLY = 0x20;
Felipe Leme51e29da2017-10-24 14:03:10 -07002118
2119 private void setState(int flags) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002120 if (sVerbose) {
2121 Log.v(TAG, "setState(" + flags + ": " + DebugUtils.flagsToString(AutofillManager.class,
2122 "SET_STATE_FLAG_", flags) + ")");
2123 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002124 synchronized (mLock) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002125 if ((flags & SET_STATE_FLAG_FOR_AUTOFILL_ONLY) != 0) {
2126 mForAugmentedAutofillOnly = true;
2127 // NOTE: returning right away as this is the only flag set, at least currently...
2128 return;
2129 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002130 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
2131 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002132 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002133 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07002134 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002135 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002136 // Reset connection to system
2137 mServiceClient = null;
Felipe Leme284ad1c2018-11-15 18:16:12 -08002138 mAugmentedAutofillServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09002139 if (mServiceClientCleaner != null) {
2140 mServiceClientCleaner.clean();
2141 mServiceClientCleaner = null;
2142 }
Joanne Chung9e247b12019-07-19 23:41:32 +08002143 notifyReenableAutofill();
Svet Ganov48f10a22017-04-26 18:49:30 -07002144 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002145 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002146 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2147 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002148 }
2149
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002150 /**
2151 * Sets a view as autofilled if the current value is the {code targetValue}.
2152 *
2153 * @param view The view that is to be autofilled
2154 * @param targetValue The value we want to fill into view
2155 */
2156 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
2157 AutofillValue currentValue = view.getAutofillValue();
2158 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002159 synchronized (mLock) {
2160 if (mLastAutofilledData == null) {
2161 mLastAutofilledData = new ParcelableMap(1);
2162 }
Felipe Leme42b97932018-02-20 13:04:31 -08002163 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002164 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002165 view.setAutofilled(true);
2166 }
2167 }
2168
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002169 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002170 synchronized (mLock) {
2171 if (sessionId != mSessionId) {
2172 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07002173 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002174
Felipe Leme637e05e2017-12-06 12:09:37 -08002175 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002176 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002177 return;
2178 }
2179
2180 final int itemCount = ids.size();
2181 int numApplied = 0;
2182 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002183 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
2184 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002185
Felipe Leme49f08ed2018-03-26 16:18:45 -07002186 ArrayList<AutofillId> failedIds = null;
2187
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002188 for (int i = 0; i < itemCount; i++) {
2189 final AutofillId id = ids.get(i);
2190 final AutofillValue value = values.get(i);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002191 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002192 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07002193 // Most likely view has been removed after the initial request was sent to the
2194 // the service; this is fine, but we need to update the view status in the
2195 // server side so it can be triggered again.
2196 Log.d(TAG, "autofill(): no View with id " + id);
2197 if (failedIds == null) {
2198 failedIds = new ArrayList<>();
2199 }
2200 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002201 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07002202 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002203 if (id.isVirtualInt()) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002204 if (virtualValues == null) {
2205 // Most likely there will be just one view with virtual children.
2206 virtualValues = new ArrayMap<>(1);
2207 }
2208 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
2209 if (valuesByParent == null) {
2210 // We don't know the size yet, but usually it will be just a few fields...
2211 valuesByParent = new SparseArray<>(5);
2212 virtualValues.put(view, valuesByParent);
2213 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002214 valuesByParent.put(id.getVirtualChildIntId(), value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002215 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002216 // Mark the view as to be autofilled with 'value'
2217 if (mLastAutofilledData == null) {
2218 mLastAutofilledData = new ParcelableMap(itemCount - i);
2219 }
2220 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002221
2222 view.autofill(value);
2223
2224 // Set as autofilled if the values match now, e.g. when the value was updated
2225 // synchronously.
2226 // If autofill happens async, the view is set to autofilled in
2227 // notifyValueChanged.
2228 setAutofilledIfValuesIs(view, value);
2229
2230 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002231 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002232 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002233
Felipe Leme49f08ed2018-03-26 16:18:45 -07002234 if (failedIds != null) {
2235 if (sVerbose) {
2236 Log.v(TAG, "autofill(): total failed views: " + failedIds);
2237 }
2238 try {
2239 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
2240 } catch (RemoteException e) {
2241 // In theory, we could ignore this error since it's not a big deal, but
2242 // in reality, we rather crash the app anyways, as the failure could be
2243 // a consequence of something going wrong on the server side...
2244 e.rethrowFromSystemServer();
2245 }
2246 }
2247
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002248 if (virtualValues != null) {
2249 for (int i = 0; i < virtualValues.size(); i++) {
2250 final View parent = virtualValues.keyAt(i);
2251 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
2252 parent.autofill(childrenValues);
2253 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07002254 // TODO: we should provide a callback so the parent can call failures; something
2255 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002256 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002257 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002258
Felipe Leme11166522018-05-07 10:18:47 -07002259 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07002260 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07002261 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002262 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002263 }
2264
Felipe Leme11166522018-05-07 10:18:47 -07002265 private LogMaker newLog(int category) {
Felipe Lemeb838a092018-05-22 14:56:15 -07002266 final LogMaker log = new LogMaker(category)
Felipe Leme833c99b2018-05-24 10:41:48 -07002267 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
2268
2269 if (isCompatibilityModeEnabledLocked()) {
2270 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
2271 }
Felipe Lemeb838a092018-05-22 14:56:15 -07002272 final AutofillClient client = getClient();
2273 if (client == null) {
2274 // Client should never be null here, but it doesn't hurt to check...
2275 log.setPackageName(mContext.getPackageName());
2276 } else {
2277 log.setComponentName(client.autofillClientGetComponentName());
2278 }
2279 return log;
Felipe Leme11166522018-05-07 10:18:47 -07002280 }
2281
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002282 /**
2283 * Set the tracked views.
2284 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002285 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002286 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002287 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07002288 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002289 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002290 */
Felipe Leme27e20222017-05-18 15:24:11 -07002291 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002292 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
2293 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Felipe Leme785777b2019-04-30 18:12:50 -07002294 if (saveTriggerId != null) {
2295 saveTriggerId.resetSessionId();
2296 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002297 synchronized (mLock) {
Felipe Leme159cb002019-03-27 10:44:59 -07002298 if (sVerbose) {
2299 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
2300 + ", trackedIds=" + Arrays.toString(trackedIds)
2301 + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible
2302 + ", saveOnFinish=" + saveOnFinish
2303 + ", fillableIds=" + Arrays.toString(fillableIds)
2304 + ", saveTrigerId=" + saveTriggerId
2305 + ", mFillableIds=" + mFillableIds
2306 + ", mEnabled=" + mEnabled
2307 + ", mSessionId=" + mSessionId);
2308
2309 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002310 if (mEnabled && mSessionId == sessionId) {
2311 if (saveOnAllViewsInvisible) {
2312 mTrackedViews = new TrackedViews(trackedIds);
2313 } else {
2314 mTrackedViews = null;
2315 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002316 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07002317 if (fillableIds != null) {
2318 if (mFillableIds == null) {
2319 mFillableIds = new ArraySet<>(fillableIds.length);
2320 }
2321 for (AutofillId id : fillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002322 id.resetSessionId();
Felipe Leme27e20222017-05-18 15:24:11 -07002323 mFillableIds.add(id);
2324 }
Felipe Leme27e20222017-05-18 15:24:11 -07002325 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002326
2327 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
2328 // Turn off trigger on previous view id.
2329 setNotifyOnClickLocked(mSaveTriggerId, false);
2330 }
2331
2332 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
2333 // Turn on trigger on new view id.
2334 mSaveTriggerId = saveTriggerId;
2335 setNotifyOnClickLocked(mSaveTriggerId, true);
2336 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002337 }
2338 }
2339 }
2340
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002341 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
2342 final View view = findView(id);
2343 if (view == null) {
2344 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
2345 return;
2346 }
2347 view.setNotifyAutofillManagerOnClick(notify);
2348 }
2349
Felipe Lemec24a56a2017-08-03 14:27:57 -07002350 private void setSaveUiState(int sessionId, boolean shown) {
2351 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
2352 synchronized (mLock) {
2353 if (mSessionId != NO_SESSION) {
2354 // Race condition: app triggered a new session after the previous session was
2355 // finished but before server called setSaveUiState() - need to cancel the new
2356 // session to avoid further inconsistent behavior.
2357 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
2358 + ") called on existing session " + mSessionId + "; cancelling it");
2359 cancelSessionLocked();
2360 }
2361 if (shown) {
2362 mSessionId = sessionId;
2363 mState = STATE_SHOWING_SAVE_UI;
2364 } else {
2365 mSessionId = NO_SESSION;
2366 mState = STATE_UNKNOWN;
2367 }
2368 }
2369 }
2370
Felipe Leme650f7ab2017-09-19 13:08:24 -07002371 /**
2372 * Marks the state of the session as finished.
2373 *
2374 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002375 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2376 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
Felipe Lemed9dc9542018-09-19 11:54:28 -07002377 * changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
2378 * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
2379 * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
2380 * service disabled further autofill requests for the activity).
Felipe Leme6e43dd32019-03-13 17:25:33 -07002381 * @param autofillableIds list of ids that could trigger autofill, use to not handle a new
2382 * session when they're entered.
Felipe Leme650f7ab2017-09-19 13:08:24 -07002383 */
Felipe Leme6e43dd32019-03-13 17:25:33 -07002384 private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002385 if (autofillableIds != null) {
2386 for (int i = 0; i < autofillableIds.size(); i++) {
2387 autofillableIds.get(i).resetSessionId();
2388 }
2389 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002390 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002391 if (sVerbose) {
2392 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
Felipe Leme6e43dd32019-03-13 17:25:33 -07002393 + getStateAsString(newState) + "; autofillableIds=" + autofillableIds);
2394 }
2395 if (autofillableIds != null) {
2396 mEnteredIds = new ArraySet<>(autofillableIds);
Felipe Leme0c8ce322018-03-23 13:54:22 -07002397 }
Felipe Lemed9dc9542018-09-19 11:54:28 -07002398 if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002399 resetSessionLocked(/* resetEnteredIds= */ true);
2400 mState = STATE_UNKNOWN;
2401 } else {
2402 resetSessionLocked(/* resetEnteredIds= */ false);
2403 mState = newState;
2404 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002405 }
2406 }
2407
Felipe Leme284ad1c2018-11-15 18:16:12 -08002408 /**
2409 * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
2410 *
2411 * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
2412 * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
2413 */
2414 private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
2415 synchronized (mLock) {
2416 if (mAugmentedAutofillServiceClient == null) {
2417 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
2418 }
2419 final Bundle resultData = new Bundle();
2420 resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
2421 mAugmentedAutofillServiceClient.asBinder());
2422
2423 try {
2424 result.send(0, resultData);
2425 } catch (RemoteException e) {
2426 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
2427 }
2428 }
2429 }
2430
Dake Gub0fa3782018-02-26 12:25:14 -08002431 /** @hide */
2432 public void requestHideFillUi() {
2433 requestHideFillUi(mIdShownFillUi, true);
2434 }
2435
2436 private void requestHideFillUi(AutofillId id, boolean force) {
2437 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002438 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002439 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002440 if (force) {
2441 // When user taps outside autofill window, force to close fill ui even id does
2442 // not match.
2443 AutofillClient client = getClient();
2444 if (client != null) {
2445 client.autofillClientRequestHideFillUi();
2446 }
2447 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002448 return;
2449 }
Felipe Leme27e20222017-05-18 15:24:11 -07002450 requestHideFillUi(id, anchor);
2451 }
2452
2453 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002454
2455 AutofillCallback callback = null;
2456 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002457 // We do not check the session id for two reasons:
2458 // 1. If local and remote session id are off sync the UI would be stuck shown
2459 // 2. There is a race between the user state being destroyed due the fill
2460 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002461 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002462 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002463 if (client.autofillClientRequestHideFillUi()) {
2464 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002465 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002466 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002467 }
2468 }
2469
2470 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002471 if (id.isVirtualInt()) {
2472 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002473 AutofillCallback.EVENT_INPUT_HIDDEN);
2474 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002475 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002476 }
2477 }
2478 }
2479
Joanne Chung9e247b12019-07-19 23:41:32 +08002480 private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
2481 synchronized (mLock) {
2482 if (mOptions == null) {
2483 return;
2484 }
2485 long expiration = SystemClock.elapsedRealtime() + disableDuration;
2486 // Protect it against overflow
2487 if (expiration < 0) {
2488 expiration = Long.MAX_VALUE;
2489 }
2490 if (componentName != null) {
2491 if (mOptions.disabledActivities == null) {
2492 mOptions.disabledActivities = new ArrayMap<>();
2493 }
2494 mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
2495 } else {
2496 mOptions.appDisabledExpiration = expiration;
2497 }
2498 }
2499 }
2500
2501 void notifyReenableAutofill() {
2502 synchronized (mLock) {
2503 if (mOptions == null) {
2504 return;
2505 }
2506 mOptions.appDisabledExpiration = 0;
2507 mOptions.disabledActivities = null;
2508 }
2509 }
2510
Felipe Leme17292d12017-10-24 14:03:10 -07002511 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002512 if (sVerbose) {
2513 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002514 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002515 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002516 final View anchor = findView(id);
2517 if (anchor == null) {
2518 return;
2519 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002520
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002521 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002522 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002523 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002524 callback = mCallback;
2525 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002526 }
2527
2528 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002529 if (id.isVirtualInt()) {
2530 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002531 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2532 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002533 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002534 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002535 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002536
Felipe Lemee5db59d2019-05-07 11:10:07 -07002537 if (sessionFinishedState != STATE_UNKNOWN) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002538 // Callback call was "hijacked" to also update the session state.
Felipe Leme6e43dd32019-03-13 17:25:33 -07002539 setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
Felipe Leme4753bb02017-03-22 20:24:00 -07002540 }
2541 }
2542
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002543 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002544 * Find a single view by its id.
2545 *
2546 * @param autofillId The autofill id of the view
2547 *
2548 * @return The view or {@code null} if view was not found
2549 */
2550 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002551 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002552 if (client != null) {
2553 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002554 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002555 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002556 }
2557
Felipe Lemebb810922017-04-25 15:54:06 -07002558 /** @hide */
2559 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002560 return mService != null;
2561 }
2562
Felipe Lemec24a56a2017-08-03 14:27:57 -07002563 /** @hide */
2564 public void onPendingSaveUi(int operation, IBinder token) {
2565 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2566
2567 synchronized (mLock) {
2568 try {
2569 mService.onPendingSaveUi(operation, token);
2570 } catch (RemoteException e) {
2571 e.rethrowFromSystemServer();
2572 }
2573 }
2574 }
2575
2576 /** @hide */
2577 public void dump(String outerPrefix, PrintWriter pw) {
2578 pw.print(outerPrefix); pw.println("AutofillManager:");
2579 final String pfx = outerPrefix + " ";
2580 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002581 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002582 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Lemeb546ca72018-08-15 08:44:12 -07002583 final AutofillClient client = getClient();
2584 if (client != null) {
2585 pw.print(pfx); pw.print("client: "); pw.print(client);
2586 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2587 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002588 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
Felipe Lemedf30f272019-03-19 13:47:48 -07002589 pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002590 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2591 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002592 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002593 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
Felipe Lemef5e26302018-06-08 16:26:39 -07002594 pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002595 pw.print(pfx); pw.print("tracked views: ");
2596 if (mTrackedViews == null) {
2597 pw.println("null");
2598 } else {
2599 final String pfx2 = pfx + " ";
2600 pw.println();
2601 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2602 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2603 }
2604 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002605 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Lemea7de4022019-02-19 17:16:45 -08002606 if (mEnteredForAugmentedAutofillIds != null) {
2607 pw.print(pfx); pw.print("entered ids for augmented autofill: ");
2608 pw.println(mEnteredForAugmentedAutofillIds);
2609 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07002610 if (mForAugmentedAutofillOnly) {
2611 pw.print(pfx); pw.println("For Augmented Autofill Only");
2612 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002613 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2614 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Lemea4f39cd2019-02-19 15:08:59 -08002615 if (mOptions != null) {
2616 pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
2617 }
Felipe Lemeb546ca72018-08-15 08:44:12 -07002618 pw.print(pfx); pw.print("compat mode enabled: ");
2619 synchronized (mLock) {
2620 if (mCompatibilityBridge != null) {
2621 final String pfx2 = pfx + " ";
2622 pw.println("true");
2623 pw.print(pfx2); pw.print("windowId: ");
2624 pw.println(mCompatibilityBridge.mFocusedWindowId);
2625 pw.print(pfx2); pw.print("nodeId: ");
2626 pw.println(mCompatibilityBridge.mFocusedNodeId);
2627 pw.print(pfx2); pw.print("virtualId: ");
2628 pw.println(AccessibilityNodeInfo
2629 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2630 pw.print(pfx2); pw.print("focusedBounds: ");
2631 pw.println(mCompatibilityBridge.mFocusedBounds);
2632 } else {
2633 pw.println("false");
2634 }
2635 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002636 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2637 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002638 }
2639
Andreas Gampe3f24e692018-02-05 13:24:28 -08002640 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002641 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002642 return getStateAsString(mState);
2643 }
2644
2645 @NonNull
2646 private static String getStateAsString(int state) {
2647 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002648 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002649 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002650 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002651 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002652 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002653 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002654 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002655 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002656 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002657 return "DISABLED_BY_SERVICE";
2658 case STATE_UNKNOWN_COMPAT_MODE:
2659 return "UNKNOWN_COMPAT_MODE";
Felipe Lemed9dc9542018-09-19 11:54:28 -07002660 case STATE_UNKNOWN_FAILED:
2661 return "UNKNOWN_FAILED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002662 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002663 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002664 }
2665 }
2666
Felipe Leme559e21d2019-01-18 17:57:21 -08002667 /** @hide */
2668 public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
Felipe Lemecc510272019-02-07 14:56:27 -08002669 switch (flags) {
2670 case FLAG_SMART_SUGGESTION_OFF:
2671 return "OFF";
2672 case FLAG_SMART_SUGGESTION_SYSTEM:
2673 return "SYSTEM";
2674 default:
2675 return "INVALID:" + flags;
2676 }
Felipe Leme559e21d2019-01-18 17:57:21 -08002677 }
2678
Andreas Gampe3f24e692018-02-05 13:24:28 -08002679 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002680 private boolean isActiveLocked() {
2681 return mState == STATE_ACTIVE;
2682 }
2683
Andreas Gampe3f24e692018-02-05 13:24:28 -08002684 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002685 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002686 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002687 }
2688
Andreas Gampe3f24e692018-02-05 13:24:28 -08002689 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002690 private boolean isFinishedLocked() {
2691 return mState == STATE_FINISHED;
2692 }
2693
Felipe Leme9876a6f2017-05-30 15:47:28 -07002694 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002695 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002696 if (client == null) {
2697 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2698 return;
2699 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002700 client.autofillClientRunOnUiThread(runnable);
2701 }
2702
2703 /**
2704 * Implementation of the accessibility based compatibility.
2705 */
2706 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2707 @GuardedBy("mLock")
2708 private final Rect mFocusedBounds = new Rect();
2709 @GuardedBy("mLock")
2710 private final Rect mTempBounds = new Rect();
2711
2712 @GuardedBy("mLock")
2713 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2714 @GuardedBy("mLock")
2715 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2716
2717 // Need to report a fake service in case a11y clients check the service list
2718 @NonNull
2719 @GuardedBy("mLock")
2720 AccessibilityServiceInfo mCompatServiceInfo;
2721
2722 CompatibilityBridge() {
2723 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2724 am.setAccessibilityPolicy(this);
2725 }
2726
2727 private AccessibilityServiceInfo getCompatServiceInfo() {
2728 synchronized (mLock) {
2729 if (mCompatServiceInfo != null) {
2730 return mCompatServiceInfo;
2731 }
2732 final Intent intent = new Intent();
2733 intent.setComponent(new ComponentName("android",
2734 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2735 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2736 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2737 try {
2738 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2739 } catch (XmlPullParserException | IOException e) {
2740 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2741 throw new IllegalStateException("Cannot find compat autofill service");
2742 }
2743 return mCompatServiceInfo;
2744 }
2745 }
2746
2747 @Override
2748 public boolean isEnabled(boolean accessibilityEnabled) {
2749 return true;
2750 }
2751
2752 @Override
2753 public int getRelevantEventTypes(int relevantEventTypes) {
2754 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2755 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002756 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002757 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2758 }
2759
2760 @Override
2761 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2762 List<AccessibilityServiceInfo> installedServices) {
2763 if (installedServices == null) {
2764 installedServices = new ArrayList<>();
2765 }
2766 installedServices.add(getCompatServiceInfo());
2767 return installedServices;
2768 }
2769
2770 @Override
2771 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2772 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2773 if (enabledService == null) {
2774 enabledService = new ArrayList<>();
2775 }
2776 enabledService.add(getCompatServiceInfo());
2777 return enabledService;
2778 }
2779
2780 @Override
2781 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2782 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002783 final int type = event.getEventType();
2784 if (sVerbose) {
2785 // NOTE: this is waaay spammy, but that's life.
2786 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme7d4461d2018-08-16 10:49:03 -07002787 + "): virtualId="
2788 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2789 + ", client=" + getClient());
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002790 }
2791 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002792 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2793 synchronized (mLock) {
2794 if (mFocusedWindowId == event.getWindowId()
2795 && mFocusedNodeId == event.getSourceNodeId()) {
2796 return event;
2797 }
2798 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2799 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2800 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2801 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2802 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2803 mFocusedBounds.set(0, 0, 0, 0);
2804 }
2805 final int windowId = event.getWindowId();
2806 final long nodeId = event.getSourceNodeId();
2807 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2808 mFocusedWindowId = windowId;
2809 mFocusedNodeId = nodeId;
2810 }
2811 }
2812 } break;
2813
2814 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2815 synchronized (mLock) {
2816 if (mFocusedWindowId == event.getWindowId()
2817 && mFocusedNodeId == event.getSourceNodeId()) {
2818 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2819 }
2820 }
2821 } break;
2822
Felipe Leme54cc6832018-03-06 12:54:31 -08002823 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2824 synchronized (mLock) {
2825 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2826 }
2827 } break;
2828
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002829 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2830 final AutofillClient client = getClient();
2831 if (client != null) {
2832 synchronized (mLock) {
2833 if (client.autofillClientIsFillUiShowing()) {
2834 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2835 }
2836 updateTrackedViewsLocked();
2837 }
2838 }
2839 } break;
2840 }
2841
2842 return accessibilityEnabled ? event : null;
2843 }
2844
2845 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2846 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2847 if (!isVirtualNode(virtualId)) {
2848 return false;
2849 }
2850 final View view = findViewByAccessibilityId(windowId, nodeId);
2851 if (view == null) {
2852 return false;
2853 }
2854 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2855 if (node == null) {
2856 return false;
2857 }
2858 if (!node.isEditable()) {
2859 return false;
2860 }
2861 final Rect newBounds = mTempBounds;
2862 node.getBoundsInScreen(newBounds);
2863 if (newBounds.equals(focusedBounds)) {
2864 return false;
2865 }
2866 focusedBounds.set(newBounds);
2867 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2868 return true;
2869 }
2870
2871 private void notifyViewExited(int windowId, long nodeId) {
2872 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2873 if (!isVirtualNode(virtualId)) {
2874 return;
2875 }
2876 final View view = findViewByAccessibilityId(windowId, nodeId);
2877 if (view == null) {
2878 return;
2879 }
2880 AutofillManager.this.notifyViewExited(view, virtualId);
2881 }
2882
2883 private void notifyValueChanged(int windowId, long nodeId) {
2884 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2885 if (!isVirtualNode(virtualId)) {
2886 return;
2887 }
2888 final View view = findViewByAccessibilityId(windowId, nodeId);
2889 if (view == null) {
2890 return;
2891 }
2892 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2893 if (node == null) {
2894 return;
2895 }
2896 AutofillManager.this.notifyValueChanged(view, virtualId,
2897 AutofillValue.forText(node.getText()));
2898 }
2899
Felipe Leme54cc6832018-03-06 12:54:31 -08002900 private void notifyViewClicked(int windowId, long nodeId) {
2901 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2902 if (!isVirtualNode(virtualId)) {
2903 return;
2904 }
2905 final View view = findViewByAccessibilityId(windowId, nodeId);
2906 if (view == null) {
2907 return;
2908 }
2909 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2910 if (node == null) {
2911 return;
2912 }
2913 AutofillManager.this.notifyViewClicked(view, virtualId);
2914 }
2915
Andreas Gampe3f24e692018-02-05 13:24:28 -08002916 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002917 private void updateTrackedViewsLocked() {
2918 if (mTrackedViews != null) {
2919 mTrackedViews.onVisibleForAutofillChangedLocked();
2920 }
2921 }
2922
2923 private View findViewByAccessibilityId(int windowId, long nodeId) {
2924 final AutofillClient client = getClient();
2925 if (client == null) {
2926 return null;
2927 }
2928 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2929 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2930 }
2931
2932 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2933 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2934 if (provider == null) {
2935 return null;
2936 }
2937 return provider.createAccessibilityNodeInfo(virtualId);
2938 }
2939
2940 private boolean isVirtualNode(int nodeId) {
2941 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2942 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2943 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002944 }
2945
Felipe Lemee6010f22017-03-03 11:19:51 -08002946 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002947 * View tracking information. Once all tracked views become invisible the session is finished.
2948 */
2949 private class TrackedViews {
2950 /** Visible tracked views */
2951 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2952
2953 /** Invisible tracked views */
2954 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2955
2956 /**
2957 * Check if set is null or value is in set.
2958 *
2959 * @param set The set or null (== empty set)
2960 * @param value The value that might be in the set
2961 *
2962 * @return {@code true} iff set is not empty and value is in set
2963 */
Felipe Leme27e20222017-05-18 15:24:11 -07002964 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002965 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2966 return set != null && set.contains(value);
2967 }
2968
2969 /**
2970 * Add a value to a set. If set is null, create a new set.
2971 *
2972 * @param set The set or null (== empty set)
2973 * @param valueToAdd The value to add
2974 *
2975 * @return The set including the new value. If set was {@code null}, a set containing only
2976 * the new value.
2977 */
Felipe Leme27e20222017-05-18 15:24:11 -07002978 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002979 @NonNull
2980 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2981 if (set == null) {
2982 set = new ArraySet<>(1);
2983 }
2984
2985 set.add(valueToAdd);
2986
2987 return set;
2988 }
2989
2990 /**
2991 * Remove a value from a set.
2992 *
2993 * @param set The set or null (== empty set)
2994 * @param valueToRemove The value to remove
2995 *
2996 * @return The set without the removed value. {@code null} if set was null, or is empty
2997 * after removal.
2998 */
Felipe Leme27e20222017-05-18 15:24:11 -07002999 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003000 @Nullable
3001 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
3002 if (set == null) {
3003 return null;
3004 }
3005
3006 set.remove(valueToRemove);
3007
3008 if (set.isEmpty()) {
3009 return null;
3010 }
3011
3012 return set;
3013 }
3014
3015 /**
3016 * Set the tracked views.
3017 *
3018 * @param trackedIds The views to be tracked
3019 */
Felipe Leme27e20222017-05-18 15:24:11 -07003020 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08003021 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003022 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003023 final boolean[] isVisible;
3024
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003025 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003026 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003027 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003028 } else {
3029 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07003030 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003031 }
3032
Felipe Leme27e20222017-05-18 15:24:11 -07003033 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003034 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07003035 final AutofillId id = trackedIds[i];
Felipe Leme785777b2019-04-30 18:12:50 -07003036 id.resetSessionId();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003037
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003038 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07003039 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003040 } else {
3041 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3042 }
3043 }
3044 }
3045
Felipe Leme9f9ee252017-04-27 13:56:22 -07003046 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003047 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003048 + " mVisibleTrackedIds=" + mVisibleTrackedIds
3049 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
3050 }
3051
3052 if (mVisibleTrackedIds == null) {
3053 finishSessionLocked();
3054 }
3055 }
3056
3057 /**
3058 * Called when a {@link View view's} visibility changes.
3059 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07003060 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003061 * @param isVisible visible if the view is visible in the view hierarchy.
3062 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003063 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08003064 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07003065 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003066 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003067 + isVisible);
3068 }
3069
Dake Gu67decfa2017-12-27 11:48:08 -08003070 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003071 if (isVisible) {
3072 if (isInSet(mInvisibleTrackedIds, id)) {
3073 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
3074 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
3075 }
3076 } else {
3077 if (isInSet(mVisibleTrackedIds, id)) {
3078 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
3079 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3080 }
3081 }
3082 }
3083
3084 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07003085 if (sVerbose) {
3086 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
3087 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003088 finishSessionLocked();
3089 }
3090 }
3091
3092 /**
3093 * Called once the client becomes visible.
3094 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003095 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003096 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003097 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003098 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003099 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003100 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08003101 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003102 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
3103 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
3104 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07003105 if (sVerbose) {
3106 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
3107 + " vis=" + mVisibleTrackedIds);
3108 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003109 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003110 final ArrayList<AutofillId> orderedInvisibleIds =
3111 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003112 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3113 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003114
3115 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
3116 for (int i = 0; i < numInvisibleTrackedIds; i++) {
3117 final AutofillId id = orderedInvisibleIds.get(i);
3118 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003119 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3120
Felipe Leme9f9ee252017-04-27 13:56:22 -07003121 if (sDebug) {
3122 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003123 }
3124 } else {
3125 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3126 }
3127 }
3128 }
3129
3130 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003131 final ArrayList<AutofillId> orderedVisibleIds =
3132 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003133 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3134 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003135
3136 final int numVisibleTrackedIds = orderedVisibleIds.size();
3137 for (int i = 0; i < numVisibleTrackedIds; i++) {
3138 final AutofillId id = orderedVisibleIds.get(i);
3139
3140 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003141 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3142 } else {
3143 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3144
Felipe Leme9f9ee252017-04-27 13:56:22 -07003145 if (sDebug) {
3146 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003147 }
3148 }
3149 }
3150 }
3151
3152 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
3153 mVisibleTrackedIds = updatedVisibleTrackedIds;
3154 }
3155
3156 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003157 if (sVerbose) {
3158 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
3159 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003160 finishSessionLocked();
3161 }
3162 }
3163 }
3164
3165 /**
Felipe Leme744976e2017-07-31 11:34:14 -07003166 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08003167 *
3168 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003169 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003170 */
3171 public abstract static class AutofillCallback {
3172
3173 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07003174 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
3175 EVENT_INPUT_SHOWN,
3176 EVENT_INPUT_HIDDEN,
3177 EVENT_INPUT_UNAVAILABLE
3178 })
Felipe Lemee6010f22017-03-03 11:19:51 -08003179 @Retention(RetentionPolicy.SOURCE)
3180 public @interface AutofillEventType {}
3181
3182 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003183 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08003184 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003185 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08003186 * should be hidden upon receiving this event.
3187 */
3188 public static final int EVENT_INPUT_SHOWN = 1;
3189
3190 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003191 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003192 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003193 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08003194 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
3195 */
3196 public static final int EVENT_INPUT_HIDDEN = 2;
3197
3198 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003199 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07003200 * autofill is not available.
3201 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003202 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07003203 * to avoid flickering, it could shown it upon receiving this event.
3204 */
3205 public static final int EVENT_INPUT_UNAVAILABLE = 3;
3206
3207 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08003208 * Called after a change in the autofill state associated with a view.
3209 *
3210 * @param view view associated with the change.
3211 *
3212 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3213 */
Felipe Leme81f01d92017-03-16 17:13:25 -07003214 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
3215 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003216
3217 /**
3218 * Called after a change in the autofill state associated with a virtual view.
3219 *
3220 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07003221 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08003222 *
3223 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3224 */
Felipe Leme6dcec872017-05-25 11:24:23 -07003225 public void onAutofillEvent(@NonNull View view, int virtualId,
3226 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07003227 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003228 }
3229
Felipe Leme640f30a2017-03-06 15:44:06 -08003230 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
3231 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00003232
Felipe Leme284ad1c2018-11-15 18:16:12 -08003233 private AutofillManagerClient(AutofillManager autofillManager) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003234 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00003235 }
3236
3237 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07003238 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003239 final AutofillManager afm = mAfm.get();
3240 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07003241 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00003242 }
3243 }
3244
3245 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003246 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003247 final AutofillManager afm = mAfm.get();
3248 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003249 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00003250 }
3251 }
3252
3253 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07003254 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08003255 Intent fillInIntent, boolean authenticateInline) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003256 final AutofillManager afm = mAfm.get();
3257 if (afm != null) {
Adam He5be0f152020-02-03 15:53:32 -08003258 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
3259 authenticateInline));
Svet Ganov782043c2017-02-11 00:52:02 +00003260 }
3261 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003262
3263 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003264 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3265 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003266 final AutofillManager afm = mAfm.get();
3267 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003268 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3269 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07003270 }
3271 }
3272
3273 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003274 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003275 final AutofillManager afm = mAfm.get();
3276 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08003277 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07003278 }
3279 }
3280
3281 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07003282 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003283 final AutofillManager afm = mAfm.get();
3284 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07003285 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08003286 }
3287 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07003288
3289 @Override
Joanne Chung9e247b12019-07-19 23:41:32 +08003290 public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
3291 throws RemoteException {
3292 final AutofillManager afm = mAfm.get();
3293 if (afm != null) {
3294 afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
3295 }
3296 }
3297
3298 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08003299 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
3300 final AutofillManager afm = mAfm.get();
3301 if (afm != null) {
3302 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
3303 }
3304 }
3305
3306 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07003307 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003308 final AutofillManager afm = mAfm.get();
3309 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003310 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003311 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07003312 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07003313 } catch (IntentSender.SendIntentException e) {
3314 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
3315 }
3316 });
3317 }
3318 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003319
3320 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07003321 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003322 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
3323 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003324 final AutofillManager afm = mAfm.get();
3325 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003326 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
3327 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003328 }
3329 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07003330
3331 @Override
3332 public void setSaveUiState(int sessionId, boolean shown) {
3333 final AutofillManager afm = mAfm.get();
3334 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003335 afm.post(() -> afm.setSaveUiState(sessionId, shown));
3336 }
3337 }
3338
3339 @Override
Felipe Leme6e43dd32019-03-13 17:25:33 -07003340 public void setSessionFinished(int newState, List<AutofillId> autofillableIds) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003341 final AutofillManager afm = mAfm.get();
3342 if (afm != null) {
Felipe Leme6e43dd32019-03-13 17:25:33 -07003343 afm.post(() -> afm.setSessionFinished(newState, autofillableIds));
Felipe Lemec24a56a2017-08-03 14:27:57 -07003344 }
3345 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003346
3347 @Override
3348 public void getAugmentedAutofillClient(IResultReceiver result) {
3349 final AutofillManager afm = mAfm.get();
3350 if (afm != null) {
3351 afm.post(() -> afm.getAugmentedAutofillClient(result));
3352 }
3353 }
3354 }
3355
3356 private static final class AugmentedAutofillManagerClient
3357 extends IAugmentedAutofillManagerClient.Stub {
3358 private final WeakReference<AutofillManager> mAfm;
3359
3360 private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
3361 mAfm = new WeakReference<>(autofillManager);
3362 }
3363
3364 @Override
3365 public Rect getViewCoordinates(@NonNull AutofillId id) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003366 final AutofillManager afm = mAfm.get();
3367 if (afm == null) return null;
3368
Feng Cao0fd56412020-02-13 19:58:30 -08003369 final View view = getView(afm, id);
Adam He234030a2019-03-15 15:57:57 -07003370 if (view == null) {
Adam He234030a2019-03-15 15:57:57 -07003371 return null;
3372 }
Feng Cao023b84c2018-12-14 15:51:17 -08003373 final Rect windowVisibleDisplayFrame = new Rect();
3374 view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
Felipe Leme284ad1c2018-11-15 18:16:12 -08003375 final int[] location = new int[2];
3376 view.getLocationOnScreen(location);
Feng Cao023b84c2018-12-14 15:51:17 -08003377 final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
3378 location[0] + view.getWidth(),
3379 location[1] - windowVisibleDisplayFrame.top + view.getHeight());
Felipe Leme284ad1c2018-11-15 18:16:12 -08003380 if (sVerbose) {
3381 Log.v(TAG, "Coordinates for " + id + ": " + rect);
3382 }
3383 return rect;
3384 }
3385
3386 @Override
3387 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
3388 final AutofillManager afm = mAfm.get();
3389 if (afm != null) {
3390 afm.post(() -> afm.autofill(sessionId, ids, values));
3391 }
3392 }
Feng Cao158b6562019-01-07 15:42:52 -08003393
3394 @Override
3395 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3396 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3397 final AutofillManager afm = mAfm.get();
3398 if (afm != null) {
3399 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3400 presenter));
3401 }
3402 }
3403
3404 @Override
3405 public void requestHideFillUi(int sessionId, AutofillId id) {
3406 final AutofillManager afm = mAfm.get();
3407 if (afm != null) {
3408 afm.post(() -> afm.requestHideFillUi(id, false));
3409 }
3410 }
Feng Cao0fd56412020-02-13 19:58:30 -08003411
3412 @Override
3413 public boolean requestAutofill(int sessionId, AutofillId id) {
3414 final AutofillManager afm = mAfm.get();
3415 if (afm == null || afm.mSessionId != sessionId) {
3416 if (sDebug) {
3417 Slog.d(TAG, "Autofill not available or sessionId doesn't match");
3418 }
3419 return false;
3420 }
3421 final View view = getView(afm, id);
3422 if (view == null || !view.isFocused()) {
3423 if (sDebug) {
3424 Slog.d(TAG, "View not available or is not on focus");
3425 }
3426 return false;
3427 }
3428 if (sVerbose) {
3429 Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
3430 }
3431 afm.post(() -> afm.requestAutofill(view));
3432 return true;
3433 }
3434
3435 @Nullable
3436 private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
3437 final AutofillClient client = afm.getClient();
3438 if (client == null) {
3439 Log.w(TAG, "getView(" + id + "): no autofill client");
3440 return null;
3441 }
3442 View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3443 if (view == null) {
3444 Log.w(TAG, "getView(" + id + "): could not find view");
3445 }
3446 return view;
3447 }
Svet Ganov782043c2017-02-11 00:52:02 +00003448 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08003449}