blob: 76fe6b5f666d9c76cceab1bab9e2454c3a480a07 [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;
Adam Heef0fe2082019-10-25 11:58:15 -070020import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
felipealfd3f8f62018-02-07 11:49:07 +010021import static android.view.autofill.Helper.sDebug;
22import static android.view.autofill.Helper.sVerbose;
Felipe Leme481db7c2019-03-14 15:50:12 -070023import static android.view.autofill.Helper.toList;
felipealfd3f8f62018-02-07 11:49:07 +010024
Svetoslav Ganov24c90452017-12-27 15:17:14 -080025import android.accessibilityservice.AccessibilityServiceInfo;
Felipe Lemee6010f22017-03-03 11:19:51 -080026import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080027import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080028import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070029import android.annotation.RequiresFeature;
Felipe Leme559e21d2019-01-18 17:57:21 -080030import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060031import android.annotation.SystemService;
Felipe Leme2cab38c2019-01-11 10:39:14 -080032import android.annotation.TestApi;
Felipe Lemea4f39cd2019-02-19 15:08:59 -080033import android.content.AutofillOptions;
Felipe Leme17292d12017-10-24 14:03:10 -070034import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000036import android.content.Intent;
37import android.content.IntentSender;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080038import android.content.pm.PackageManager;
39import android.content.pm.ResolveInfo;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070041import android.metrics.LogMaker;
Felipe Leme68b22222018-07-24 14:57:01 -070042import android.os.Build;
Svet Ganov782043c2017-02-11 00:52:02 +000043import android.os.Bundle;
Feng Cao43c20432020-03-26 17:57:34 -070044import android.os.Handler;
Felipe Lemec24a56a2017-08-03 14:27:57 -070045import android.os.IBinder;
Feng Cao43c20432020-03-26 17:57:34 -070046import android.os.Looper;
Svet Ganov782043c2017-02-11 00:52:02 +000047import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080048import android.os.RemoteException;
Joanne Chung9e247b12019-07-19 23:41:32 +080049import android.os.SystemClock;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070050import android.service.autofill.AutofillService;
51import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080052import android.service.autofill.UserData;
lpetere28d6b02019-11-27 15:02:42 +080053import android.text.TextUtils;
Felipe Leme4753bb02017-03-22 20:24:00 -070054import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070055import android.util.ArraySet;
Felipe Lemee5db59d2019-05-07 11:10:07 -070056import android.util.DebugUtils;
Felipe Leme3461d3c2017-01-19 08:54:55 -080057import android.util.Log;
Felipe Lemece404982018-09-17 09:46:13 -070058import android.util.Slog;
Felipe Leme4753bb02017-03-22 20:24:00 -070059import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080060import android.view.Choreographer;
Dake Gu6a20a192018-02-08 12:09:30 -080061import android.view.KeyEvent;
Felipe Leme3461d3c2017-01-19 08:54:55 -080062import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080063import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityManager;
65import android.view.accessibility.AccessibilityNodeInfo;
66import android.view.accessibility.AccessibilityNodeProvider;
67import android.view.accessibility.AccessibilityWindowInfo;
Feng Cao43c20432020-03-26 17:57:34 -070068import android.view.inputmethod.InputMethodManager;
lpetere28d6b02019-11-27 15:02:42 +080069import android.widget.EditText;
Adam Heef0fe2082019-10-25 11:58:15 -070070import android.widget.TextView;
felipealfd3f8f62018-02-07 11:49:07 +010071
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070072import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070073import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070074import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Lemed4e52282018-06-18 13:56:38 -070075import com.android.internal.os.IResultReceiver;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080076import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080077import com.android.internal.util.Preconditions;
Felipe Lemec0c15a32019-01-08 10:53:33 -080078import com.android.internal.util.SyncResultReceiver;
felipealfd3f8f62018-02-07 11:49:07 +010079
Svetoslav Ganov24c90452017-12-27 15:17:14 -080080import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070081
Svetoslav Ganov24c90452017-12-27 15:17:14 -080082import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070083import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080084import java.lang.annotation.Retention;
85import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000086import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070087import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080088import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080089import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000090import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070091import java.util.Objects;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080092import java.util.Set;
Felipe Lemec0c15a32019-01-08 10:53:33 -080093
felipealfd3f8f62018-02-07 11:49:07 +010094import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080095
Adam He5be0f152020-02-03 15:53:32 -080096//TODO: use java.lang.ref.Cleaner once Android supports Java 9
97
Felipe Leme3461d3c2017-01-19 08:54:55 -080098/**
Laura Davis43e75d92018-08-10 15:46:06 -070099 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
100 * integrate with the Autofill Framework lifecycle.
101 *
102 * <p>To learn about using Autofill in your app, read
103 * the <a href="/guide/topics/text/autofill">Autofill Framework</a> guides.
104 *
105 * <h3 id="autofill-lifecycle">Autofill lifecycle</h3>
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700106 *
Felipe Leme744976e2017-07-31 11:34:14 -0700107 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
Laura Davis43e75d92018-08-10 15:46:06 -0700108 * activity context. The autofill context is created when one of the following methods is called for
Felipe Leme744976e2017-07-31 11:34:14 -0700109 * the first time in an activity context, and the current user has an enabled autofill service:
110 *
111 * <ul>
112 * <li>{@link #notifyViewEntered(View)}
113 * <li>{@link #notifyViewEntered(View, int, Rect)}
114 * <li>{@link #requestAutofill(View)}
115 * </ul>
116 *
Laura Davis43e75d92018-08-10 15:46:06 -0700117 * <p>Typically, the context is automatically created when the first view of the activity is
Felipe Leme744976e2017-07-31 11:34:14 -0700118 * focused because {@code View.onFocusChanged()} indirectly calls
119 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
120 * explicitly create it (for example, a custom view developer could offer a contextual menu action
121 * in a text-field view to let users manually request autofill).
122 *
123 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
124 * that represents the view hierarchy by calling
125 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
126 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
127 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
128 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
129 * the hierarchy.
130 *
131 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
132 * parses it looking for views that can be autofilled. If the service finds such views, it returns
133 * a data structure to the Android System containing the following optional info:
134 *
135 * <ul>
136 * <li>Datasets used to autofill subsets of views in the activity.
137 * <li>Id of views that the service can save their values for future autofilling.
138 * </ul>
139 *
140 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700141 * UI associated with the view, when the view is focused on and is part of a dataset.
142 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700143 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700144 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700145 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
146 *
147 * <p>When the service returns ids of savable views, the Android System keeps track of changes
148 * made to these views, so they can be used to determine if the autofill save UI is shown later.
149 *
150 * <p>The context is then finished when one of the following occurs:
151 *
152 * <ul>
153 * <li>{@link #commit()} is called or all savable views are gone.
154 * <li>{@link #cancel()} is called.
155 * </ul>
156 *
157 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700158 * 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 -0700159 * option to Save, the current value of the views is then sent to the autofill service.
160 *
Laura Davis43e75d92018-08-10 15:46:06 -0700161 * <h3 id="additional-notes">Additional notes</h3>
162 *
163 * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800164 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600165@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700166@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
Felipe Leme640f30a2017-03-06 15:44:06 -0800167public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800168
Felipe Leme640f30a2017-03-06 15:44:06 -0800169 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800170
Svet Ganov782043c2017-02-11 00:52:02 +0000171 /**
172 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700173 *
Svet Ganov782043c2017-02-11 00:52:02 +0000174 * <p>
175 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000176 */
177 public static final String EXTRA_ASSIST_STRUCTURE =
178 "android.view.autofill.extra.ASSIST_STRUCTURE";
179
180 /**
181 * Intent extra: The result of an authentication operation. It is
182 * either a fully populated {@link android.service.autofill.FillResponse}
183 * or a fully populated {@link android.service.autofill.Dataset} if
184 * a response or a dataset is being authenticated respectively.
185 *
186 * <p>
187 * Type: {@link android.service.autofill.FillResponse} or a
188 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000189 */
190 public static final String EXTRA_AUTHENTICATION_RESULT =
191 "android.view.autofill.extra.AUTHENTICATION_RESULT";
192
Felipe Leme7320ca92017-03-29 15:09:54 -0700193 /**
194 * Intent extra: The optional extras provided by the
195 * {@link android.service.autofill.AutofillService}.
196 *
197 * <p>For example, when the service responds to a {@link
198 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
199 * a {@code FillResponse} that requires authentication, the Intent that launches the
200 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700201 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700202 *
Felipe Lemea9372382017-10-09 14:42:36 -0700203 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
204 * can also add this bundle to the {@link Intent} set as the
205 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
206 * so the bundle can be recovered later on
207 * {@link android.service.autofill.SaveRequest#getClientState()}.
208 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700209 * <p>
210 * Type: {@link android.os.Bundle}
211 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700212 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600213 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700214
Felipe Lemec24a56a2017-08-03 14:27:57 -0700215 /** @hide */
216 public static final String EXTRA_RESTORE_SESSION_TOKEN =
217 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
218
TYM Tsai8af721d2019-07-24 17:11:59 +0800219 /** @hide */
220 public static final String EXTRA_RESTORE_CROSS_ACTIVITY =
221 "android.view.autofill.extra.RESTORE_CROSS_ACTIVITY";
222
Felipe Leme284ad1c2018-11-15 18:16:12 -0800223 /**
224 * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
225 *
226 * @hide
227 */
228 public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
229 "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
230
Felipe Lemec24a56a2017-08-03 14:27:57 -0700231 private static final String SESSION_ID_TAG = "android:sessionId";
232 private static final String STATE_TAG = "android:state";
233 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
234
Felipe Leme0aa4c502017-04-26 12:36:01 -0700235 /** @hide */ public static final int ACTION_START_SESSION = 1;
236 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
237 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
238 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
TYM Tsai88398352020-01-14 19:49:05 +0800239 /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800240
Felipe Leme68b22222018-07-24 14:57:01 -0700241 /** @hide */ public static final int NO_LOGGING = 0;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700242 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
243 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
244 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
Felipe Lemedf30f272019-03-19 13:47:48 -0700245 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
Felipe Lemeadb34f52019-03-15 16:24:26 -0700246
Felipe Lemee5db59d2019-05-07 11:10:07 -0700247 // NOTE: flag below is used by the session start receiver only, hence it can have values above
248 /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
Felipe Lemeadb34f52019-03-15 16:24:26 -0700249
Felipe Leme68b22222018-07-24 14:57:01 -0700250 /** @hide */
251 public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
252 ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
253 : AutofillManager.NO_LOGGING;
254
255 /** @hide */
256 public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
Felipe Leme9f9ee252017-04-27 13:56:22 -0700257
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700258 /** Which bits in an authentication id are used for the dataset id */
259 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
260 /** How many bits in an authentication id are used for the dataset id */
261 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
262 /** @hide The index for an undefined data set */
263 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
264
265 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700266 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
267 *
268 * @hide
269 */
270 public static final int PENDING_UI_OPERATION_CANCEL = 1;
271
272 /**
273 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
274 *
275 * @hide
276 */
277 public static final int PENDING_UI_OPERATION_RESTORE = 2;
278
279 /**
280 * Initial state of the autofill context, set when there is no session (i.e., when
281 * {@link #mSessionId} is {@link #NO_SESSION}).
282 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700283 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
284 * the server.
285 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700286 * @hide
287 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700288 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700289
290 /**
291 * State where the autofill context hasn't been {@link #commit() finished} nor
292 * {@link #cancel() canceled} yet.
293 *
294 * @hide
295 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700296 public static final int STATE_ACTIVE = 1;
297
298 /**
299 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700300 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700301 *
302 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
303 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
304 *
305 * @hide
306 */
307 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700308
309 /**
310 * State where the autofill context has been {@link #commit() finished} but the server still has
311 * a session because the Save UI hasn't been dismissed yet.
312 *
313 * @hide
314 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700315 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700316
317 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700318 * State where the autofill is disabled because the service cannot autofill the activity at all.
319 *
320 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
321 * (and {@link #requestAutofill(View, int, Rect)}).
322 *
323 * @hide
324 */
325 public static final int STATE_DISABLED_BY_SERVICE = 4;
326
327 /**
Felipe Leme0c8ce322018-03-23 13:54:22 -0700328 * Same as {@link #STATE_UNKNOWN}, but used on
Felipe Lemeadb34f52019-03-15 16:24:26 -0700329 * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
330 * because the URL bar changed on client mode
Felipe Leme0c8ce322018-03-23 13:54:22 -0700331 *
332 * @hide
333 */
334 public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
335
Felipe Lemed9dc9542018-09-19 11:54:28 -0700336 /**
337 * Same as {@link #STATE_UNKNOWN}, but used on
Felipe Lemeadb34f52019-03-15 16:24:26 -0700338 * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
339 * because the service failed to fullfil a request.
Felipe Lemed9dc9542018-09-19 11:54:28 -0700340 *
341 * @hide
342 */
343 public static final int STATE_UNKNOWN_FAILED = 6;
Felipe Leme0c8ce322018-03-23 13:54:22 -0700344
345 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800346 * Timeout in ms for calls to the field classification service.
347 * @hide
348 */
349 public static final int FC_SERVICE_TIMEOUT = 5000;
350
351 /**
Felipe Lemec0c15a32019-01-08 10:53:33 -0800352 * Timeout for calls to system_server.
353 */
354 private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
355
356 /**
Felipe Leme2cab38c2019-01-11 10:39:14 -0800357 * @hide
358 */
359 @TestApi
360 public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
361
362 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800363 * Disables Augmented Autofill.
364 *
365 * @hide
366 */
367 @TestApi
368 public static final int FLAG_SMART_SUGGESTION_OFF = 0x0;
369
370 /**
Felipe Leme559e21d2019-01-18 17:57:21 -0800371 * Displays the Augment Autofill window using the same mechanism (such as a popup-window
372 * attached to the focused view) as the standard autofill.
373 *
374 * @hide
375 */
376 @TestApi
377 public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x1;
378
Felipe Leme559e21d2019-01-18 17:57:21 -0800379 /** @hide */
Felipe Lemecc510272019-02-07 14:56:27 -0800380 @IntDef(flag = false, value = { FLAG_SMART_SUGGESTION_OFF, FLAG_SMART_SUGGESTION_SYSTEM })
Felipe Leme559e21d2019-01-18 17:57:21 -0800381 @Retention(RetentionPolicy.SOURCE)
382 public @interface SmartSuggestionMode {}
383
384 /**
Felipe Lemecc510272019-02-07 14:56:27 -0800385 * {@code DeviceConfig} property used to set which Smart Suggestion modes for Augmented Autofill
386 * are available.
Felipe Leme7841d022019-02-07 13:02:18 -0800387 *
388 * @hide
389 */
390 @TestApi
391 public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
392 "smart_suggestion_supported_modes";
393
Felipe Lemea8209102019-02-21 18:01:47 -0800394 /**
395 * Sets how long (in ms) the augmented autofill service is bound while idle.
396 *
397 * <p>Use {@code 0} to keep it permanently bound.
398 *
399 * @hide
400 */
401 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
402 "augmented_service_idle_unbind_timeout";
403
404 /**
405 * Sets how long (in ms) the augmented autofill service request is killed if not replied.
406 *
407 * @hide
408 */
409 public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
410 "augmented_service_request_timeout";
411
Felipe Leme80e7bf12019-02-14 10:56:52 -0800412 /** @hide */
413 public static final int RESULT_OK = 0;
414 /** @hide */
415 public static final int RESULT_CODE_NOT_SERVICE = -1;
416
Felipe Leme7841d022019-02-07 13:02:18 -0800417 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700418 * Makes an authentication id from a request id and a dataset id.
419 *
420 * @param requestId The request id.
421 * @param datasetId The dataset id.
422 * @return The authentication id.
423 * @hide
424 */
425 public static int makeAuthenticationId(int requestId, int datasetId) {
426 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
427 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
428 }
429
430 /**
431 * Gets the request id from an authentication id.
432 *
433 * @param authRequestId The authentication id.
434 * @return The request id.
435 * @hide
436 */
437 public static int getRequestIdFromAuthenticationId(int authRequestId) {
438 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
439 }
440
441 /**
442 * Gets the dataset id from an authentication id.
443 *
444 * @param authRequestId The authentication id.
445 * @return The dataset id.
446 * @hide
447 */
448 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
449 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
450 }
451
Felipe Leme4753bb02017-03-22 20:24:00 -0700452 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000453
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700454 /**
455 * There is currently no session running.
456 * {@hide}
457 */
Felipe Lemeadb34f52019-03-15 16:24:26 -0700458 public static final int NO_SESSION = Integer.MAX_VALUE;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700459
Svet Ganov782043c2017-02-11 00:52:02 +0000460 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700461
462 private final Object mLock = new Object();
463
464 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000465 private IAutoFillManagerClient mServiceClient;
466
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700467 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900468 private Cleaner mServiceClientCleaner;
469
470 @GuardedBy("mLock")
Felipe Leme284ad1c2018-11-15 18:16:12 -0800471 private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
472
473 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800474 private AutofillCallback mCallback;
475
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700476 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000477
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700478 @GuardedBy("mLock")
479 private int mSessionId = NO_SESSION;
480
481 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700482 private int mState = STATE_UNKNOWN;
483
484 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000485 private boolean mEnabled;
486
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700487 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700488 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700489 @Nullable private ParcelableMap mLastAutofilledData;
490
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700491 /** If view tracking is enabled, contains the tracking state */
492 @GuardedBy("mLock")
493 @Nullable private TrackedViews mTrackedViews;
494
Felipe Leme27e20222017-05-18 15:24:11 -0700495 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
496 @GuardedBy("mLock")
497 @Nullable private ArraySet<AutofillId> mFillableIds;
498
Dake Gub0fa3782018-02-26 12:25:14 -0800499 /** id of last requested autofill ui */
500 @Nullable private AutofillId mIdShownFillUi;
501
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800502 /**
503 * Views that were already "entered" - if they're entered again when the session is not active,
504 * they're ignored
505 * */
506 @GuardedBy("mLock")
507 @Nullable private ArraySet<AutofillId> mEnteredIds;
508
Felipe Lemea7de4022019-02-19 17:16:45 -0800509 /**
510 * Views that were otherwised not important for autofill but triggered a session because the
511 * context is whitelisted for augmented autofill.
512 */
513 @GuardedBy("mLock")
514 @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
515
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700516 /** If set, session is commited when the field is clicked. */
517 @GuardedBy("mLock")
518 @Nullable private AutofillId mSaveTriggerId;
519
Dake Gu67decfa2017-12-27 11:48:08 -0800520 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
521 @GuardedBy("mLock")
522 private boolean mOnInvisibleCalled;
523
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700524 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
525 @GuardedBy("mLock")
526 private boolean mSaveOnFinish;
527
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800528 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
529 @GuardedBy("mLock")
530 private CompatibilityBridge mCompatibilityBridge;
531
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800532 @Nullable
533 private final AutofillOptions mOptions;
534
Felipe Lemeadb34f52019-03-15 16:24:26 -0700535 /** When set, session is only used for augmented autofill requests. */
536 @GuardedBy("mLock")
537 private boolean mForAugmentedAutofillOnly;
538
Felipe Lemedf30f272019-03-19 13:47:48 -0700539 /**
Felipe Lemee5db59d2019-05-07 11:10:07 -0700540 * When set, standard autofill is disabled, but sessions can still be created for augmented
Felipe Lemedf30f272019-03-19 13:47:48 -0700541 * autofill only.
542 */
543 @GuardedBy("mLock")
544 private boolean mEnabledForAugmentedAutofillOnly;
545
Svet Ganov782043c2017-02-11 00:52:02 +0000546 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800547 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000548 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000549 * Asks the client to start an authentication flow.
550 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700551 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000552 * @param intent The authentication intent.
553 * @param fillInIntent The authentication fill-in intent.
554 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800555 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -0800556 Intent fillInIntent, boolean authenticateInline);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800557
558 /**
559 * Tells the client this manager has state to be reset.
560 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800561 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700562
563 /**
564 * Request showing the autofill UI.
565 *
566 * @param anchor The real view the UI needs to anchor to.
567 * @param width The width of the fill UI content.
568 * @param height The height of the fill UI content.
569 * @param virtualBounds The bounds of the virtual decendant of the anchor.
570 * @param presenter The presenter that controls the fill UI window.
571 * @return Whether the UI was shown.
572 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800573 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700574 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
575
576 /**
Dake Gu6a20a192018-02-08 12:09:30 -0800577 * Dispatch unhandled keyevent from Autofill window
578 * @param anchor The real view the UI needs to anchor to.
579 * @param keyEvent Unhandled KeyEvent from autofill window.
580 */
581 void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
582
583 /**
Felipe Leme4753bb02017-03-22 20:24:00 -0700584 * Request hiding the autofill UI.
585 *
586 * @return Whether the UI was hidden.
587 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800588 boolean autofillClientRequestHideFillUi();
589
590 /**
591 * Gets whether the fill UI is currenlty being shown.
592 *
593 * @return Whether the fill UI is currently being shown
594 */
595 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700596
597 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700598 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700599 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700600 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700601 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800602 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700603
604 /**
605 * Checks is the client is currently visible as understood by autofill.
606 *
607 * @return {@code true} if the client is currently visible
608 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800609 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700610
611 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800612 * Client might disable enter/exit event e.g. when activity is paused.
613 */
614 boolean isDisablingEnterExitEventForAutofill();
615
616 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700617 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700618 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800619 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700620 *
Felipe Leme27e20222017-05-18 15:24:11 -0700621 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700622 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800623 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
624 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700625
626 /**
627 * Finds a view by traversing the hierarchies of the client.
628 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800629 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700630 *
631 * @return The view, or {@code null} if not found
632 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800633 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
634
635 /**
636 * Finds a view by a11y id in a given client window.
637 *
638 * @param viewId The accessibility id of the views to find
639 * @param windowId The accessibility window id where to search
640 *
641 * @return The view, or {@code null} if not found
642 */
643 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700644
645 /**
646 * Runs the specified action on the UI thread.
647 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800648 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700649
650 /**
651 * Gets the complete component name of this client.
652 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800653 ComponentName autofillClientGetComponentName();
654
655 /**
656 * Gets the activity token
657 */
658 @Nullable IBinder autofillClientGetActivityToken();
659
660 /**
661 * @return Whether compatibility mode is enabled.
662 */
Felipe Leme42b97932018-02-20 13:04:31 -0800663 boolean autofillClientIsCompatibilityModeEnabled();
664
665 /**
666 * Gets the next unique autofill ID.
667 *
668 * <p>Typically used to manage views whose content is recycled - see
669 * {@link View#setAutofillId(AutofillId)} for more info.
670 *
671 * @return An ID that is unique in the activity.
672 */
673 @Nullable AutofillId autofillClientGetNextAutofillId();
Svet Ganov782043c2017-02-11 00:52:02 +0000674 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800675
676 /**
677 * @hide
678 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800679 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800680 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800681 mService = service;
Felipe Lemea4f39cd2019-02-19 15:08:59 -0800682 mOptions = context.getAutofillOptions();
683
684 if (mOptions != null) {
685 sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
686 sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
687 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800688 }
689
690 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800691 * @hide
692 */
693 public void enableCompatibilityMode() {
694 synchronized (mLock) {
695 // The accessibility manager is a singleton so we may need to plug
696 // different bridge based on which activity is currently focused
697 // in the current process. Since compat would be rarely used, just
698 // create and register a new instance every time.
Felipe Lemece404982018-09-17 09:46:13 -0700699 if (sDebug) {
700 Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
701 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800702 mCompatibilityBridge = new CompatibilityBridge();
703 }
704 }
705
706 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700707 * Restore state after activity lifecycle
708 *
709 * @param savedInstanceState The state to be restored
710 *
711 * {@hide}
712 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700713 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700714 if (!hasAutofillFeature()) {
715 return;
716 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700717 synchronized (mLock) {
718 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
719
Felipe Lemec24a56a2017-08-03 14:27:57 -0700720 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700721 Log.w(TAG, "New session was started before onCreate()");
722 return;
723 }
724
725 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700726 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700727
728 if (mSessionId != NO_SESSION) {
729 ensureServiceClientAddedIfNeededLocked();
730
Felipe Leme637e05e2017-12-06 12:09:37 -0800731 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700732 if (client != null) {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800733 final SyncResultReceiver receiver = new SyncResultReceiver(
734 SYNC_CALLS_TIMEOUT_MS);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700735 try {
Felipe Lemed4e52282018-06-18 13:56:38 -0700736 mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
737 mServiceClient.asBinder(), receiver);
738 final boolean sessionWasRestored = receiver.getIntResult() == 1;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700739
740 if (!sessionWasRestored) {
741 Log.w(TAG, "Session " + mSessionId + " could not be restored");
742 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700743 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700744 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700745 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700746 Log.d(TAG, "session " + mSessionId + " was restored");
747 }
748
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800749 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700750 }
751 } catch (RemoteException e) {
752 Log.e(TAG, "Could not figure out if there was an autofill session", e);
753 }
754 }
755 }
756 }
757 }
758
759 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700760 * Called once the client becomes visible.
761 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800762 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700763 *
764 * {@hide}
765 */
766 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800767 // This gets called when the client just got visible at which point the visibility
768 // of the tracked views may not have been computed (due to a pending layout, etc).
769 // While generally we have no way to know when the UI has settled. We will evaluate
770 // the tracked views state at the end of next frame to guarantee that everything
771 // that may need to be laid out is laid out.
772 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
773 synchronized (mLock) {
774 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
775 mTrackedViews.onVisibleForAutofillChangedLocked();
776 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700777 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800778 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700779 }
780
781 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800782 * Called once the client becomes invisible.
783 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800784 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800785 *
TYM Tsai88398352020-01-14 19:49:05 +0800786 * @param isExpiredResponse The response has expired or not
787 *
Dake Gu67decfa2017-12-27 11:48:08 -0800788 * {@hide}
789 */
TYM Tsai88398352020-01-14 19:49:05 +0800790 public void onInvisibleForAutofill(boolean isExpiredResponse) {
Dake Gu67decfa2017-12-27 11:48:08 -0800791 synchronized (mLock) {
792 mOnInvisibleCalled = true;
TYM Tsai88398352020-01-14 19:49:05 +0800793
794 if (isExpiredResponse) {
795 // Notify service the response has expired.
796 updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
797 ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
798 }
Dake Gu67decfa2017-12-27 11:48:08 -0800799 }
800 }
801
802 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700803 * Save state before activity lifecycle
804 *
805 * @param outState Place to store the state
806 *
807 * {@hide}
808 */
809 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700810 if (!hasAutofillFeature()) {
811 return;
812 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700813 synchronized (mLock) {
814 if (mSessionId != NO_SESSION) {
815 outState.putInt(SESSION_ID_TAG, mSessionId);
816 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700817 if (mState != STATE_UNKNOWN) {
818 outState.putInt(STATE_TAG, mState);
819 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700820 if (mLastAutofilledData != null) {
821 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
822 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700823 }
824 }
825
826 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800827 * @hide
828 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800829 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100830 public boolean isCompatibilityModeEnabledLocked() {
831 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800832 }
833
834 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700835 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700836 *
837 * <p>Typically used to determine whether the option to explicitly request autofill should
838 * be offered - see {@link #requestAutofill(View)}.
839 *
840 * @return whether autofill is enabled for the current user.
841 */
842 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800843 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700844 return false;
845 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700846 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800847 if (isDisabledByServiceLocked()) {
848 return false;
849 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700850 ensureServiceClientAddedIfNeededLocked();
851 return mEnabled;
852 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700853 }
854
855 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700856 * Should always be called from {@link AutofillService#getFillEventHistory()}.
857 *
858 * @hide
859 */
860 @Nullable public FillEventHistory getFillEventHistory() {
861 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -0800862 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -0700863 mService.getFillEventHistory(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -0800864 return receiver.getParcelableResult();
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700865 } catch (RemoteException e) {
866 e.rethrowFromSystemServer();
867 return null;
868 }
869 }
870
871 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700872 * Explicitly requests a new autofill context.
873 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700874 * <p>Normally, the autofill context is automatically started if necessary when
875 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
876 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
877 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700878 *
879 * @param view view requesting the new autofill context.
880 */
881 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700882 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700883 }
884
885 /**
Feng Cao08abd462020-04-20 17:47:27 -0700886 * Explicitly cancels the current session and requests a new autofill context.
887 *
888 * <p>Normally, the autofill context is automatically started if necessary when
889 * {@link #notifyViewEntered(View)} is called, but this method should be used in
890 * cases where it must be explicitly started or restarted. Currently, this method should only
891 * be called by
892 * {@link android.service.autofill.augmented.AugmentedAutofillService#requestAutofill(
893 * ComponentName, AutofillId)} to cancel the current session and trigger the autofill flow in
894 * a new session, giving the autofill service or the augmented autofill service a chance to
895 * send updated suggestions.
896 *
897 * @param view view requesting the new autofill context.
898 */
899 void requestAutofillFromNewSession(@NonNull View view) {
900 cancel();
901 notifyViewEntered(view);
902 }
903
904 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700905 * Explicitly requests a new autofill context for virtual views.
906 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700907 * <p>Normally, the autofill context is automatically started if necessary when
908 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
909 * cases where it must be explicitly started. For example, when the virtual view offers an
910 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700911 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700912 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
913 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
914 * the absolute bounds could be calculated by:
915 *
916 * <pre class="prettyprint">
917 * int offset[] = new int[2];
918 * getLocationOnScreen(offset);
919 * Rect absBounds = new Rect(bounds.left + offset[0],
920 * bounds.top + offset[1],
921 * bounds.right + offset[0], bounds.bottom + offset[1]);
922 * </pre>
923 *
924 * @param view the virtual view parent.
925 * @param virtualId id identifying the virtual child inside the parent view.
926 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700927 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700928 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
929 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700930 }
931
Felipe Leme2ac463e2017-03-13 14:06:25 -0700932 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700933 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800934 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700935 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800936 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700937 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700938 notifyViewEntered(view, 0);
939 }
940
Andreas Gampe3f24e692018-02-05 13:24:28 -0800941 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800942 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800943 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700944 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800945 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
946 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700947 }
948 return true;
949 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800950 if (isFinishedLocked()) {
951 // Session already finished: ignore if automatic request and view already entered
952 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
953 && mEnteredIds.contains(id)) {
954 if (sVerbose) {
955 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
956 + ") on state " + getStateAsStringLocked()
957 + " because view was already entered: " + mEnteredIds);
958 }
959 return true;
960 }
961 }
Felipe Leme17292d12017-10-24 14:03:10 -0700962 return false;
963 }
964
Dake Gu67decfa2017-12-27 11:48:08 -0800965 private boolean isClientVisibleForAutofillLocked() {
966 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800967 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800968 }
969
970 private boolean isClientDisablingEnterExitEvent() {
971 final AutofillClient client = getClient();
972 return client != null && client.isDisablingEnterExitEventForAutofill();
973 }
974
Felipe Lemed1146422017-04-26 10:17:05 -0700975 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700976 if (!hasAutofillFeature()) {
977 return;
978 }
Dake Gu67decfa2017-12-27 11:48:08 -0800979 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700980 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800981 callback = notifyViewEnteredLocked(view, flags);
982 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700983
Dake Gu67decfa2017-12-27 11:48:08 -0800984 if (callback != null) {
985 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
986 }
987 }
Svet Ganov782043c2017-02-11 00:52:02 +0000988
Dake Gu67decfa2017-12-27 11:48:08 -0800989 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800990 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800991 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800992 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800993 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800994
995 AutofillCallback callback = null;
996
997 ensureServiceClientAddedIfNeededLocked();
998
Felipe Lemedf30f272019-03-19 13:47:48 -0700999 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001000 if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1001
Dake Gu67decfa2017-12-27 11:48:08 -08001002 if (mCallback != null) {
1003 callback = mCallback;
1004 }
1005 } else {
1006 // don't notify entered when Activity is already in background
1007 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001008 final AutofillValue value = view.getAutofillValue();
1009
Adam Heef0fe2082019-10-25 11:58:15 -07001010 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1011 flags |= FLAG_PASSWORD_INPUT_TYPE;
1012 }
1013
Felipe Lemec24a56a2017-08-03 14:27:57 -07001014 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001015 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001016 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001017 } else {
1018 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -07001019 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1020 if (sDebug) {
1021 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1022 + "mForAugmentedAutofillOnly on manual request");
1023 }
1024 mForAugmentedAutofillOnly = false;
1025 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001026 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001027 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001028 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001029 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001030 }
Dake Gu67decfa2017-12-27 11:48:08 -08001031 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -08001032 }
1033
1034 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001035 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001036 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001037 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001038 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001039 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001040 if (!hasAutofillFeature()) {
1041 return;
1042 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001043 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001044 notifyViewExitedLocked(view);
1045 }
1046 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001047
Andreas Gampe3f24e692018-02-05 13:24:28 -08001048 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001049 void notifyViewExitedLocked(@NonNull View view) {
1050 ensureServiceClientAddedIfNeededLocked();
1051
Felipe Lemedf30f272019-03-19 13:47:48 -07001052 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001053 // dont notify exited when Activity is already in background
1054 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -08001055 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -08001056
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001057 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001058 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001059 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001060 }
1061 }
1062
1063 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001064 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001065 *
1066 * @param view {@link View} that was exited.
1067 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001068 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001069 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1070 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1071 }
1072
1073 /**
1074 * Called when a virtual view's visibility changed.
1075 *
1076 * @param view {@link View} that was exited.
1077 * @param virtualId id identifying the virtual child inside the parent view.
1078 * @param isVisible visible if the view is visible in the view hierarchy.
1079 */
1080 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1081 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1082 }
1083
1084 /**
1085 * Called when a view/virtual view's visibility changed.
1086 *
1087 * @param view {@link View} that was exited.
1088 * @param virtualId id identifying the virtual child inside the parent view.
1089 * @param isVisible visible if the view is visible in the view hierarchy.
1090 * @param virtual Whether the view is virtual.
1091 */
1092 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1093 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001094 synchronized (mLock) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001095 if (mForAugmentedAutofillOnly) {
1096 if (sVerbose) {
1097 Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
1098 }
1099 return;
1100 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001101 if (mEnabled && isActiveLocked()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001102 final AutofillId id = virtual ? getAutofillId(view, virtualId)
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001103 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -08001104 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -07001105 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001106 if (mFillableIds.contains(id)) {
1107 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1108 requestHideFillUi(id, view);
1109 }
1110 }
1111 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001112 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -08001113 } else if (sVerbose) {
1114 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -07001115 }
lpetere28d6b02019-11-27 15:02:42 +08001116 } else if (!virtual && isVisible) {
1117 startAutofillIfNeededLocked(view);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001118 }
1119 }
1120 }
1121
1122 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001123 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001124 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001125 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1126 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1127 * the absolute bounds could be calculated by:
1128 *
1129 * <pre class="prettyprint">
1130 * int offset[] = new int[2];
1131 * getLocationOnScreen(offset);
1132 * Rect absBounds = new Rect(bounds.left + offset[0],
1133 * bounds.top + offset[1],
1134 * bounds.right + offset[0], bounds.bottom + offset[1]);
1135 * </pre>
1136 *
1137 * @param view the virtual view parent.
1138 * @param virtualId id identifying the virtual child inside the parent view.
1139 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -08001140 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001141 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1142 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -07001143 }
1144
Felipe Leme6dcec872017-05-25 11:24:23 -07001145 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -07001146 if (!hasAutofillFeature()) {
1147 return;
1148 }
Dake Gu67decfa2017-12-27 11:48:08 -08001149 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001150 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001151 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
1152 }
Felipe Leme17292d12017-10-24 14:03:10 -07001153
Dake Gu67decfa2017-12-27 11:48:08 -08001154 if (callback != null) {
1155 callback.onAutofillEvent(view, virtualId,
1156 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1157 }
1158 }
Svet Ganov782043c2017-02-11 00:52:02 +00001159
Dake Gu67decfa2017-12-27 11:48:08 -08001160 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001161 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001162 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
1163 int flags) {
Felipe Leme305fd402018-09-11 18:20:42 +00001164 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -08001165 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001166 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -08001167
1168 ensureServiceClientAddedIfNeededLocked();
1169
Felipe Lemedf30f272019-03-19 13:47:48 -07001170 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemef3b844b2018-10-02 13:50:50 -07001171 if (sVerbose) {
1172 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1173 }
Dake Gu67decfa2017-12-27 11:48:08 -08001174 if (mCallback != null) {
1175 callback = mCallback;
1176 }
1177 } else {
1178 // don't notify entered when Activity is already in background
1179 if (!isClientDisablingEnterExitEvent()) {
Adam Heef0fe2082019-10-25 11:58:15 -07001180 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1181 flags |= FLAG_PASSWORD_INPUT_TYPE;
1182 }
1183
Felipe Lemec24a56a2017-08-03 14:27:57 -07001184 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001185 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001186 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001187 } else {
1188 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -07001189 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1190 if (sDebug) {
1191 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1192 + "mForAugmentedAutofillOnly on manual request");
1193 }
1194 mForAugmentedAutofillOnly = false;
1195 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001196 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001197 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001198 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001199 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001200 }
Dake Gu67decfa2017-12-27 11:48:08 -08001201 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -08001202 }
1203
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001204 @GuardedBy("mLock")
1205 private void addEnteredIdLocked(@NonNull AutofillId id) {
1206 if (mEnteredIds == null) {
1207 mEnteredIds = new ArraySet<>(1);
1208 }
Felipe Leme785777b2019-04-30 18:12:50 -07001209 id.resetSessionId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001210 mEnteredIds.add(id);
1211 }
1212
Philip P. Moltmann44611812017-02-23 12:52:46 -08001213 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001214 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001215 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001216 * @param view the virtual view parent.
1217 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001218 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001219 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001220 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001221 if (!hasAutofillFeature()) {
1222 return;
1223 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001224 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001225 notifyViewExitedLocked(view, virtualId);
1226 }
1227 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001228
Andreas Gampe3f24e692018-02-05 13:24:28 -08001229 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001230 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1231 ensureServiceClientAddedIfNeededLocked();
1232
Felipe Lemedf30f272019-03-19 13:47:48 -07001233 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001234 // don't notify exited when Activity is already in background
1235 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001236 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001237
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001238 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001239 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001240 }
Svet Ganov782043c2017-02-11 00:52:02 +00001241 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001242 }
1243
1244 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001245 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001246 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001247 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001248 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001249 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001250 if (!hasAutofillFeature()) {
1251 return;
1252 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001253 AutofillId id = null;
1254 boolean valueWasRead = false;
1255 AutofillValue value = null;
1256
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001257 synchronized (mLock) {
1258 // If the session is gone some fields might still be highlighted, hence we have to
1259 // remove the isAutofilled property even if no sessions are active.
1260 if (mLastAutofilledData == null) {
Adam He5061db82020-03-11 15:58:27 -07001261 view.setAutofilled(false, false);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001262 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001263 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001264 if (mLastAutofilledData.containsKey(id)) {
1265 value = view.getAutofillValue();
1266 valueWasRead = true;
Adam Hee2dbbce2020-03-25 13:30:24 -07001267 final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001268
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001269 if (Objects.equals(mLastAutofilledData.get(id), value)) {
Adam Hee2dbbce2020-03-25 13:30:24 -07001270 view.setAutofilled(true, hideHighlight);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001271 } else {
Adam He5061db82020-03-11 15:58:27 -07001272 view.setAutofilled(false, false);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001273 mLastAutofilledData.remove(id);
1274 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001275 } else {
Adam He5061db82020-03-11 15:58:27 -07001276 view.setAutofilled(false, false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001277 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001278 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001279
Felipe Lemec24a56a2017-08-03 14:27:57 -07001280 if (!mEnabled || !isActiveLocked()) {
lpetere28d6b02019-11-27 15:02:42 +08001281 if (!startAutofillIfNeededLocked(view)) {
1282 if (sVerbose) {
1283 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1284 + "): ignoring on state " + getStateAsStringLocked());
1285 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001286 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001287 return;
1288 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001289
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001290 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001291 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001292 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001293
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001294 if (!valueWasRead) {
1295 value = view.getAutofillValue();
1296 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001297
Felipe Leme0aa4c502017-04-26 12:36:01 -07001298 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001299 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001300 }
1301
Felipe Lemebab851c2017-02-03 18:45:08 -08001302 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001303 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001304 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001305 * @param view the virtual view parent.
1306 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001307 * @param value new value of the child.
1308 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001309 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001310 if (!hasAutofillFeature()) {
1311 return;
1312 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001313 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001314 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001315 if (sVerbose) {
1316 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1317 + "): ignoring on state " + getStateAsStringLocked());
1318 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001319 return;
1320 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001321
Felipe Leme305fd402018-09-11 18:20:42 +00001322 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001323 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001324 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001325 }
1326
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001327 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001328 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001329 *
Felipe Leme67e62092018-02-15 14:47:31 -08001330 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001331 */
Felipe Leme67e62092018-02-15 14:47:31 -08001332 public void notifyViewClicked(@NonNull View view) {
1333 notifyViewClicked(view.getAutofillId());
1334 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001335
Felipe Leme67e62092018-02-15 14:47:31 -08001336 /**
1337 * Called to indicate a virtual view has been clicked.
1338 *
1339 * @param view the virtual view parent.
1340 * @param virtualId id identifying the virtual child inside the parent view.
1341 */
1342 public void notifyViewClicked(@NonNull View view, int virtualId) {
Felipe Leme305fd402018-09-11 18:20:42 +00001343 notifyViewClicked(getAutofillId(view, virtualId));
Felipe Leme67e62092018-02-15 14:47:31 -08001344 }
1345
1346 private void notifyViewClicked(AutofillId id) {
1347 if (!hasAutofillFeature()) {
1348 return;
1349 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001350 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1351
1352 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001353 if (!mEnabled || !isActiveLocked()) {
1354 return;
1355 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001356 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1357 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1358 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001359 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001360 }
1361 }
1362 }
1363
1364 /**
1365 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1366 *
1367 * @hide
1368 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001369 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001370 if (!hasAutofillFeature()) {
1371 return;
1372 }
1373 synchronized (mLock) {
1374 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001375 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001376 commitLocked();
1377 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001378 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001379 cancelLocked();
1380 }
1381 }
1382 }
1383
Felipe Lemebab851c2017-02-03 18:45:08 -08001384 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001385 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001386 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001387 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1388 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1389 * that represent the HTML elements, it should call this method after the form is submitted and
1390 * another page is rendered.
1391 *
1392 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1393 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001394 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001395 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001396 if (!hasAutofillFeature()) {
1397 return;
1398 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001399 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001400 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001401 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001402 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001403 }
1404
Andreas Gampe3f24e692018-02-05 13:24:28 -08001405 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001406 private void commitLocked() {
1407 if (!mEnabled && !isActiveLocked()) {
1408 return;
1409 }
1410 finishSessionLocked();
1411 }
1412
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001413 /**
1414 * Called to indicate the current autofill context should be cancelled.
1415 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001416 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1417 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1418 * that represent the HTML elements, it should call this method if the user does not post the
1419 * form but moves to another form in this page.
1420 *
1421 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1422 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001423 */
1424 public void cancel() {
Feng Cao08abd462020-04-20 17:47:27 -07001425 if (sVerbose) Log.v(TAG, "cancel() called by app or augmented autofill service");
Svet Ganov43574b02017-04-12 09:25:20 -07001426 if (!hasAutofillFeature()) {
1427 return;
1428 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001429 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001430 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001431 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001432 }
1433
Andreas Gampe3f24e692018-02-05 13:24:28 -08001434 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001435 private void cancelLocked() {
1436 if (!mEnabled && !isActiveLocked()) {
1437 return;
1438 }
1439 cancelSessionLocked();
1440 }
1441
Svet Ganovf965b872017-04-28 16:34:02 -07001442 /** @hide */
1443 public void disableOwnedAutofillServices() {
1444 disableAutofillServices();
1445 }
1446
Svet Ganovf20a0372017-04-10 17:08:05 -07001447 /**
1448 * If the app calling this API has enabled autofill services they
1449 * will be disabled.
1450 */
Svet Ganovf965b872017-04-28 16:34:02 -07001451 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001452 if (!hasAutofillFeature()) {
1453 return;
1454 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001455 try {
1456 mService.disableOwnedAutofillServices(mContext.getUserId());
1457 } catch (RemoteException e) {
1458 throw e.rethrowFromSystemServer();
1459 }
1460 }
1461
Felipe Lemedb041182017-04-21 17:33:38 -07001462 /**
1463 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1464 * enabled for the current user, or {@code false} otherwise.
1465 */
1466 public boolean hasEnabledAutofillServices() {
1467 if (mService == null) return false;
1468
Felipe Lemec0c15a32019-01-08 10:53:33 -08001469 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001470 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001471 mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
1472 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001473 } catch (RemoteException e) {
1474 throw e.rethrowFromSystemServer();
1475 }
1476 }
1477
1478 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001479 * Returns the component name of the {@link AutofillService} that is enabled for the current
1480 * user.
1481 */
1482 @Nullable
1483 public ComponentName getAutofillServiceComponentName() {
1484 if (mService == null) return null;
1485
Felipe Lemec0c15a32019-01-08 10:53:33 -08001486 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme23c75ff2017-12-14 13:27:44 -08001487 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001488 mService.getAutofillServiceComponentName(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001489 return receiver.getParcelableResult();
Felipe Leme23c75ff2017-12-14 13:27:44 -08001490 } catch (RemoteException e) {
1491 throw e.rethrowFromSystemServer();
1492 }
1493 }
1494
1495 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001496 * Gets the id of the {@link UserData} used for
1497 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1498 *
1499 * <p>This method is useful when the service must check the status of the {@link UserData} in
1500 * the device without fetching the whole object.
1501 *
1502 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1503 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1504 * the user.
1505 *
1506 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1507 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1508 * service for the user.
1509 */
1510 @Nullable public String getUserDataId() {
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.getUserDataId(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001514 return receiver.getStringResult();
Felipe Lemef0baef742018-01-26 14:39:39 -08001515 } catch (RemoteException e) {
1516 e.rethrowFromSystemServer();
1517 return null;
1518 }
1519 }
1520
1521 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001522 * Gets the user data used for
1523 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001524 *
Felipe Leme27f45732017-12-22 09:05:22 -08001525 * <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 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1530 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001531 */
Felipe Leme452886a2017-11-27 13:09:13 -08001532 @Nullable public UserData getUserData() {
1533 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001534 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001535 mService.getUserData(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001536 return receiver.getParcelableResult();
Felipe Leme452886a2017-11-27 13:09:13 -08001537 } catch (RemoteException e) {
1538 e.rethrowFromSystemServer();
1539 return null;
1540 }
1541 }
1542
1543 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001544 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001545 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001546 *
1547 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1548 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1549 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001550 */
Felipe Leme452886a2017-11-27 13:09:13 -08001551 public void setUserData(@Nullable UserData userData) {
1552 try {
1553 mService.setUserData(userData);
1554 } catch (RemoteException e) {
1555 e.rethrowFromSystemServer();
1556 }
1557 }
1558
1559 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001560 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1561 * enabled.
1562 *
1563 * <p>As field classification is an expensive operation, it could be disabled, either
1564 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1565 * permanently (for example, because the device is a low-level device).
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.
Felipe Leme329d0402017-12-06 09:22:43 -08001570 */
Felipe Leme329d0402017-12-06 09:22:43 -08001571 public boolean isFieldClassificationEnabled() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001572 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme329d0402017-12-06 09:22:43 -08001573 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001574 mService.isFieldClassificationEnabled(receiver);
1575 return receiver.getIntResult() == 1;
Felipe Leme329d0402017-12-06 09:22:43 -08001576 } catch (RemoteException e) {
1577 e.rethrowFromSystemServer();
1578 return false;
1579 }
1580 }
1581
1582 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001583 * Gets the name of the default algorithm used for
1584 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1585 *
1586 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1587 * set.
1588 *
1589 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1590 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1591 * the user.
1592 */
1593 @Nullable
1594 public String getDefaultFieldClassificationAlgorithm() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001595 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001596 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001597 mService.getDefaultFieldClassificationAlgorithm(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001598 return receiver.getStringResult();
Felipe Leme27f45732017-12-22 09:05:22 -08001599 } catch (RemoteException e) {
1600 e.rethrowFromSystemServer();
1601 return null;
1602 }
1603 }
1604
1605 /**
1606 * Gets the name of all algorithms currently available for
1607 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1608 *
1609 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001610 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1611 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001612 */
1613 @NonNull
1614 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001615 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001616 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001617 mService.getAvailableFieldClassificationAlgorithms(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001618 final String[] algorithms = receiver.getStringArrayResult();
Felipe Lemee4ac7402018-01-16 19:37:00 -08001619 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001620 } catch (RemoteException e) {
1621 e.rethrowFromSystemServer();
1622 return null;
1623 }
1624 }
1625
1626 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001627 * Returns {@code true} if autofill is supported by the current device and
1628 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001629 *
1630 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1631 * <ol>
1632 * <li>Low-end devices.
1633 * <li>Device policy rules that forbid its usage.
1634 * </ol>
1635 */
1636 public boolean isAutofillSupported() {
1637 if (mService == null) return false;
1638
Felipe Lemec0c15a32019-01-08 10:53:33 -08001639 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001640 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001641 mService.isServiceSupported(mContext.getUserId(), receiver);
1642 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001643 } catch (RemoteException e) {
1644 throw e.rethrowFromSystemServer();
1645 }
1646 }
1647
Felipe Leme637e05e2017-12-06 12:09:37 -08001648 // Note: don't need to use locked suffix because mContext is final.
1649 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001650 final AutofillClient client = mContext.getAutofillClient();
Felipe Leme261e75f2018-12-17 10:15:08 -08001651 if (client == null && sVerbose) {
1652 Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
Felipe Leme686128e2017-10-17 14:02:20 -07001653 + mContext);
1654 }
1655 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001656 }
1657
Dake Gud9dbd272018-03-13 11:38:42 -07001658 /**
1659 * Check if autofill ui is showing, must be called on UI thread.
1660 * @hide
1661 */
1662 public boolean isAutofillUiShowing() {
1663 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001664 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001665 }
1666
Svet Ganov782043c2017-02-11 00:52:02 +00001667 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001668 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001669 if (!hasAutofillFeature()) {
1670 return;
1671 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001672 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001673 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1674 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1675 // service set the extra and returned RESULT_CANCELED...
1676
Felipe Leme1ad95f02018-06-15 13:07:34 -07001677 if (sDebug) {
1678 Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
1679 }
Felipe Lemed633f072017-02-14 10:17:17 -08001680
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001681 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001682 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001683 return;
1684 }
Dake Gu67decfa2017-12-27 11:48:08 -08001685 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1686 // of app activity. We enforce enter event to re-show fill ui in such case.
1687 // CTS example:
1688 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1689 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1690 if (!mOnInvisibleCalled && focusView != null
1691 && focusView.canNotifyAutofillEnterExitEvent()) {
1692 notifyViewExitedLocked(focusView);
1693 notifyViewEnteredLocked(focusView, 0);
1694 }
1695 if (data == null) {
1696 // data is set to null when result is not RESULT_OK
1697 return;
1698 }
1699
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001700 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1701 final Bundle responseData = new Bundle();
1702 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001703 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1704 if (newClientState != null) {
1705 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1706 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001707 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001708 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1709 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001710 } catch (RemoteException e) {
1711 Log.e(TAG, "Error delivering authentication result", e);
1712 }
Svet Ganov782043c2017-02-11 00:52:02 +00001713 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001714 }
1715
Felipe Leme42b97932018-02-20 13:04:31 -08001716 /**
1717 * Gets the next unique autofill ID for the activity context.
1718 *
1719 * <p>Typically used to manage views whose content is recycled - see
1720 * {@link View#setAutofillId(AutofillId)} for more info.
1721 *
1722 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1723 * the {@link Context} associated with this {@link AutofillManager}.
1724 */
1725 @Nullable
1726 public AutofillId getNextAutofillId() {
1727 final AutofillClient client = getClient();
1728 if (client == null) return null;
1729
1730 final AutofillId id = client.autofillClientGetNextAutofillId();
1731
1732 if (id == null && sDebug) {
1733 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1734 }
1735
1736 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001737 }
1738
Felipe Leme305fd402018-09-11 18:20:42 +00001739 private static AutofillId getAutofillId(View parent, int virtualId) {
1740 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001741 }
1742
Andreas Gampe3f24e692018-02-05 13:24:28 -08001743 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001744 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1745 @NonNull AutofillValue value, int flags) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001746 if (mEnteredForAugmentedAutofillIds != null
Felipe Lemedf30f272019-03-19 13:47:48 -07001747 && mEnteredForAugmentedAutofillIds.contains(id)
1748 || mEnabledForAugmentedAutofillOnly) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001749 if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
Felipe Lemedf30f272019-03-19 13:47:48 -07001750 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
Felipe Lemea7de4022019-02-19 17:16:45 -08001751 }
Felipe Leme9f9ee252017-04-27 13:56:22 -07001752 if (sVerbose) {
1753 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001754 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001755 + ", compatMode=" + isCompatibilityModeEnabledLocked()
Felipe Lemedf30f272019-03-19 13:47:48 -07001756 + ", augmentedOnly=" + mForAugmentedAutofillOnly
1757 + ", enabledAugmentedOnly=" + mEnabledForAugmentedAutofillOnly
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001758 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001759 }
Felipe Lemee5db59d2019-05-07 11:10:07 -07001760 // We need to reset the augmented-only state when a manual request is made, as it's possible
1761 // that the service returned null for the first request and now the user is manually
1762 // requesting autofill to trigger a custom UI provided by the service.
1763 if (mForAugmentedAutofillOnly && !mEnabledForAugmentedAutofillOnly
1764 && (flags & FLAG_MANUAL_REQUEST) != 0) {
1765 if (sVerbose) {
1766 Log.v(TAG, "resetting mForAugmentedAutofillOnly on manual autofill request");
1767 }
1768 mForAugmentedAutofillOnly = false;
1769 }
Felipe Leme3103c632017-12-18 15:05:14 -08001770 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001771 if (sVerbose) {
1772 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001773 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001774 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001775 return;
1776 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001777 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001778 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001779 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001780
Felipe Lemec0c15a32019-01-08 10:53:33 -08001781 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemeadb34f52019-03-15 16:24:26 -07001782 final ComponentName componentName = client.autofillClientGetComponentName();
Joanne Chung9e247b12019-07-19 23:41:32 +08001783
1784 if (!mEnabledForAugmentedAutofillOnly && mOptions != null
1785 && mOptions.isAutofillDisabledLocked(componentName)) {
1786 if (mOptions.isAugmentedAutofillEnabled(mContext)) {
1787 if (sDebug) {
1788 Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
1789 + "whitelisted for augmented autofill");
1790 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
1791 }
1792 } else {
1793 if (sDebug) {
1794 Log.d(TAG, "startSession(" + componentName + "): ignored because "
1795 + "disabled by service and not whitelisted for augmented autofill");
1796 }
1797 setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
1798 client.autofillClientResetableStateAvailable();
1799 return;
1800 }
1801 }
1802
Felipe Lemed4e52282018-06-18 13:56:38 -07001803 mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001804 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Lemeadb34f52019-03-15 16:24:26 -07001805 mCallback != null, flags, componentName,
Felipe Lemed4e52282018-06-18 13:56:38 -07001806 isCompatibilityModeEnabledLocked(), receiver);
1807 mSessionId = receiver.getIntResult();
Felipe Lemec24a56a2017-08-03 14:27:57 -07001808 if (mSessionId != NO_SESSION) {
1809 mState = STATE_ACTIVE;
1810 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07001811 final int extraFlags = receiver.getOptionalExtraIntResult(0);
Felipe Lemee5db59d2019-05-07 11:10:07 -07001812 if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001813 if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
1814 mForAugmentedAutofillOnly = true;
1815 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001816 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001817 } catch (RemoteException e) {
1818 throw e.rethrowFromSystemServer();
Joanne Chung1676bec2020-02-12 11:36:30 +08001819 } catch (SyncResultReceiver.TimeoutException e) {
1820 // no-op, just log the error message.
1821 Log.w(TAG, "Exception getting result from SyncResultReceiver: " + e);
Svet Ganov782043c2017-02-11 00:52:02 +00001822 }
1823 }
1824
Andreas Gampe3f24e692018-02-05 13:24:28 -08001825 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001826 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001827 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001828
1829 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001830
Svet Ganov782043c2017-02-11 00:52:02 +00001831 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001832 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001833 } catch (RemoteException e) {
1834 throw e.rethrowFromSystemServer();
1835 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001836
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001837 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001838 }
1839
Andreas Gampe3f24e692018-02-05 13:24:28 -08001840 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001841 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001842 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001843
1844 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001845
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001846 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001847 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001848 } catch (RemoteException e) {
1849 throw e.rethrowFromSystemServer();
1850 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001851
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001852 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001853 }
1854
Andreas Gampe3f24e692018-02-05 13:24:28 -08001855 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001856 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001857 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001858 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001859 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001860 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001861 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001862 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001863 if (resetEnteredIds) {
1864 mEnteredIds = null;
1865 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001866 }
1867
Andreas Gampe3f24e692018-02-05 13:24:28 -08001868 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001869 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1870 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001871 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001872 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1873 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001874 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001875 try {
Felipe Lemed62b5162018-06-06 15:08:25 -07001876 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1877 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -08001878 } catch (RemoteException e) {
1879 throw e.rethrowFromSystemServer();
1880 }
1881 }
Svet Ganov782043c2017-02-11 00:52:02 +00001882
Andreas Gampe3f24e692018-02-05 13:24:28 -08001883 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001884 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Lemedf30f272019-03-19 13:47:48 -07001885 final AutofillClient client = getClient();
1886 if (client == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001887 return;
1888 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001889
Svet Ganov782043c2017-02-11 00:52:02 +00001890 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001891 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001892 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001893 final int userId = mContext.getUserId();
Felipe Lemec0c15a32019-01-08 10:53:33 -08001894 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedf30f272019-03-19 13:47:48 -07001895 mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
1896 userId, receiver);
Joanne Chungee556162020-05-05 19:59:30 +08001897 int flags = 0;
1898 try {
1899 flags = receiver.getIntResult();
1900 } catch (SyncResultReceiver.TimeoutException e) {
1901 Log.w(TAG, "Failed to initialize autofill: " + e);
1902 return;
1903 }
Felipe Leme9f9ee252017-04-27 13:56:22 -07001904 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1905 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1906 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Felipe Lemedf30f272019-03-19 13:47:48 -07001907 mEnabledForAugmentedAutofillOnly = (flags
1908 & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
1909 if (sVerbose) {
1910 Log.v(TAG, "receiver results: flags=" + flags + " enabled=" + mEnabled
1911 + ", enabledForAugmentedOnly: " + mEnabledForAugmentedAutofillOnly);
1912 }
Koji Fukuiccec6a62017-10-18 17:48:40 +09001913 final IAutoFillManager service = mService;
1914 final IAutoFillManagerClient serviceClient = mServiceClient;
1915 mServiceClientCleaner = Cleaner.create(this, () -> {
Felipe Leme559e21d2019-01-18 17:57:21 -08001916 // TODO(b/123100811): call service to also remove reference to
Felipe Leme284ad1c2018-11-15 18:16:12 -08001917 // mAugmentedAutofillServiceClient
Koji Fukuiccec6a62017-10-18 17:48:40 +09001918 try {
1919 service.removeClient(serviceClient, userId);
1920 } catch (RemoteException e) {
1921 }
1922 });
Svet Ganov782043c2017-02-11 00:52:02 +00001923 } catch (RemoteException e) {
1924 throw e.rethrowFromSystemServer();
1925 }
1926 }
1927 }
1928
lpetere28d6b02019-11-27 15:02:42 +08001929 @GuardedBy("mLock")
1930 private boolean startAutofillIfNeededLocked(View view) {
1931 if (mState == STATE_UNKNOWN
1932 && mSessionId == NO_SESSION
1933 && view instanceof EditText
1934 && !TextUtils.isEmpty(((EditText) view).getText())
1935 && !view.isFocused()
1936 && view.isImportantForAutofill()
1937 && view.isLaidOut()
1938 && view.isVisibleToUser()) {
1939
1940 ensureServiceClientAddedIfNeededLocked();
1941
1942 if (sVerbose) {
1943 Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
1944 }
1945 if (mEnabled && !isClientDisablingEnterExitEvent()) {
1946 final AutofillId id = view.getAutofillId();
1947 final AutofillValue value = view.getAutofillValue();
1948 // Starts new session.
1949 startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
1950 // Updates value.
1951 updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
1952 /* flags= */ 0);
1953 addEnteredIdLocked(id);
1954 return true;
1955 }
1956 }
1957 return false;
1958 }
1959
Felipe Lemee6010f22017-03-03 11:19:51 -08001960 /**
1961 * Registers a {@link AutofillCallback} to receive autofill events.
1962 *
1963 * @param callback callback to receive events.
1964 */
1965 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001966 if (!hasAutofillFeature()) {
1967 return;
1968 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001969 synchronized (mLock) {
1970 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001971
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001972 final boolean hadCallback = mCallback != null;
1973 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001974
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001975 if (!hadCallback) {
1976 try {
1977 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1978 } catch (RemoteException e) {
1979 throw e.rethrowFromSystemServer();
1980 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001981 }
1982 }
1983 }
1984
1985 /**
1986 * Unregisters a {@link AutofillCallback} to receive autofill events.
1987 *
1988 * @param callback callback to stop receiving events.
1989 */
1990 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001991 if (!hasAutofillFeature()) {
1992 return;
1993 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001994 synchronized (mLock) {
1995 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001996
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001997 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001998
Felipe Lemee6010f22017-03-03 11:19:51 -08001999 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002000 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08002001 } catch (RemoteException e) {
2002 throw e.rethrowFromSystemServer();
2003 }
2004 }
2005 }
2006
Felipe Leme559e21d2019-01-18 17:57:21 -08002007 /**
Felipe Leme559e21d2019-01-18 17:57:21 -08002008 * Explicitly limits augmented autofill to the given packages and activities.
2009 *
Felipe Leme559e21d2019-01-18 17:57:21 -08002010 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
2011 *
2012 * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
2013 * apps that uses addresses. For example, if the service wants to support augmented autofill on
2014 * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
2015 * of {@code AddressApp2}, it would call:
2016 * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
2017 * Arrays.asList(new ComponentName("AddressApp2", "act1"),
2018 * new ComponentName("AddressApp2", "act2")));}
2019 *
2020 * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
2021 * service, and it's ignored if the caller isn't it.
2022 *
2023 * @hide
2024 */
2025 @SystemApi
2026 @TestApi
Felipe Leme7a3c9f52019-02-13 16:32:49 -08002027 public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
2028 @Nullable Set<ComponentName> activities) {
Felipe Leme80e7bf12019-02-14 10:56:52 -08002029 if (!hasAutofillFeature()) {
2030 return;
2031 }
2032
2033 final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2034 final int resultCode;
2035 try {
2036 mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
2037 resultReceiver);
2038 resultCode = resultReceiver.getIntResult();
2039 } catch (RemoteException e) {
2040 throw e.rethrowFromSystemServer();
2041 }
2042 switch (resultCode) {
2043 case RESULT_OK:
2044 return;
2045 case RESULT_CODE_NOT_SERVICE:
2046 throw new SecurityException("caller is not user's Augmented Autofill Service");
2047 default:
2048 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
2049 + resultCode);
2050 }
2051 }
2052
Felipe Lemea7de4022019-02-19 17:16:45 -08002053 /**
2054 * Notifies that a non-autofillable view was entered because the activity is whitelisted for
2055 * augmented autofill.
2056 *
2057 * <p>This method is necessary to set the right flag on start, so the server-side session
2058 * doesn't trigger the standard autofill workflow, but the augmented's instead.
2059 *
2060 * @hide
2061 */
2062 public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
2063 final AutofillId id = view.getAutofillId();
2064 synchronized (mLock) {
2065 if (mEnteredForAugmentedAutofillIds == null) {
2066 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
2067 }
2068 mEnteredForAugmentedAutofillIds.add(id);
2069 }
2070 }
2071
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002072 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2073 Rect anchorBounds, IAutofillWindowPresenter presenter) {
2074 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07002075 if (anchor == null) {
2076 return;
2077 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002078
2079 AutofillCallback callback = null;
2080 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002081 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002082 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002083
2084 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002085 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08002086 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002087 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08002088 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002089 }
2090 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002091 }
2092 }
2093
2094 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002095 if (id.isVirtualInt()) {
2096 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002097 AutofillCallback.EVENT_INPUT_SHOWN);
2098 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002099 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002100 }
2101 }
2102 }
2103
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002104 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08002105 Intent fillInIntent, boolean authenticateInline) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002106 synchronized (mLock) {
2107 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002108 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002109 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08002110 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
2111 // before onAuthenticationResult()
2112 mOnInvisibleCalled = false;
Adam He5be0f152020-02-03 15:53:32 -08002113 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
2114 authenticateInline);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002115 }
2116 }
2117 }
2118 }
2119
Dake Gu6a20a192018-02-08 12:09:30 -08002120 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
2121 final View anchor = findView(id);
2122 if (anchor == null) {
2123 return;
2124 }
2125
Dake Gu6a20a192018-02-08 12:09:30 -08002126 synchronized (mLock) {
2127 if (mSessionId == sessionId) {
2128 AutofillClient client = getClient();
2129
2130 if (client != null) {
2131 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
2132 }
2133 }
2134 }
2135 }
2136
Felipe Leme51e29da2017-10-24 14:03:10 -07002137 /** @hide */
2138 public static final int SET_STATE_FLAG_ENABLED = 0x01;
2139 /** @hide */
2140 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
2141 /** @hide */
2142 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
2143 /** @hide */
2144 public static final int SET_STATE_FLAG_DEBUG = 0x08;
2145 /** @hide */
2146 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
Felipe Lemee5db59d2019-05-07 11:10:07 -07002147 /** @hide */
2148 public static final int SET_STATE_FLAG_FOR_AUTOFILL_ONLY = 0x20;
Felipe Leme51e29da2017-10-24 14:03:10 -07002149
2150 private void setState(int flags) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002151 if (sVerbose) {
2152 Log.v(TAG, "setState(" + flags + ": " + DebugUtils.flagsToString(AutofillManager.class,
2153 "SET_STATE_FLAG_", flags) + ")");
2154 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002155 synchronized (mLock) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002156 if ((flags & SET_STATE_FLAG_FOR_AUTOFILL_ONLY) != 0) {
2157 mForAugmentedAutofillOnly = true;
2158 // NOTE: returning right away as this is the only flag set, at least currently...
2159 return;
2160 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002161 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
2162 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002163 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002164 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07002165 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002166 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002167 // Reset connection to system
2168 mServiceClient = null;
Felipe Leme284ad1c2018-11-15 18:16:12 -08002169 mAugmentedAutofillServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09002170 if (mServiceClientCleaner != null) {
2171 mServiceClientCleaner.clean();
2172 mServiceClientCleaner = null;
2173 }
Joanne Chung9e247b12019-07-19 23:41:32 +08002174 notifyReenableAutofill();
Svet Ganov48f10a22017-04-26 18:49:30 -07002175 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002176 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002177 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2178 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002179 }
2180
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002181 /**
2182 * Sets a view as autofilled if the current value is the {code targetValue}.
2183 *
2184 * @param view The view that is to be autofilled
2185 * @param targetValue The value we want to fill into view
2186 */
Adam He5061db82020-03-11 15:58:27 -07002187 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
2188 boolean hideHighlight) {
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002189 AutofillValue currentValue = view.getAutofillValue();
2190 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002191 synchronized (mLock) {
2192 if (mLastAutofilledData == null) {
2193 mLastAutofilledData = new ParcelableMap(1);
2194 }
Felipe Leme42b97932018-02-20 13:04:31 -08002195 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002196 }
Adam He5061db82020-03-11 15:58:27 -07002197 view.setAutofilled(true, hideHighlight);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002198 }
2199 }
2200
Adam He5061db82020-03-11 15:58:27 -07002201 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
2202 boolean hideHighlight) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002203 synchronized (mLock) {
2204 if (sessionId != mSessionId) {
2205 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07002206 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002207
Felipe Leme637e05e2017-12-06 12:09:37 -08002208 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002209 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002210 return;
2211 }
2212
2213 final int itemCount = ids.size();
2214 int numApplied = 0;
2215 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002216 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
2217 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002218
Felipe Leme49f08ed2018-03-26 16:18:45 -07002219 ArrayList<AutofillId> failedIds = null;
2220
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002221 for (int i = 0; i < itemCount; i++) {
2222 final AutofillId id = ids.get(i);
2223 final AutofillValue value = values.get(i);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002224 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002225 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07002226 // Most likely view has been removed after the initial request was sent to the
2227 // the service; this is fine, but we need to update the view status in the
2228 // server side so it can be triggered again.
2229 Log.d(TAG, "autofill(): no View with id " + id);
2230 if (failedIds == null) {
2231 failedIds = new ArrayList<>();
2232 }
2233 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002234 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07002235 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002236 if (id.isVirtualInt()) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002237 if (virtualValues == null) {
2238 // Most likely there will be just one view with virtual children.
2239 virtualValues = new ArrayMap<>(1);
2240 }
2241 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
2242 if (valuesByParent == null) {
2243 // We don't know the size yet, but usually it will be just a few fields...
2244 valuesByParent = new SparseArray<>(5);
2245 virtualValues.put(view, valuesByParent);
2246 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002247 valuesByParent.put(id.getVirtualChildIntId(), value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002248 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002249 // Mark the view as to be autofilled with 'value'
2250 if (mLastAutofilledData == null) {
2251 mLastAutofilledData = new ParcelableMap(itemCount - i);
2252 }
2253 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002254
2255 view.autofill(value);
2256
2257 // Set as autofilled if the values match now, e.g. when the value was updated
2258 // synchronously.
2259 // If autofill happens async, the view is set to autofilled in
2260 // notifyValueChanged.
Adam He5061db82020-03-11 15:58:27 -07002261 setAutofilledIfValuesIs(view, value, hideHighlight);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002262
2263 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002264 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002265 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002266
Felipe Leme49f08ed2018-03-26 16:18:45 -07002267 if (failedIds != null) {
2268 if (sVerbose) {
2269 Log.v(TAG, "autofill(): total failed views: " + failedIds);
2270 }
2271 try {
2272 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
2273 } catch (RemoteException e) {
2274 // In theory, we could ignore this error since it's not a big deal, but
2275 // in reality, we rather crash the app anyways, as the failure could be
2276 // a consequence of something going wrong on the server side...
2277 e.rethrowFromSystemServer();
2278 }
2279 }
2280
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002281 if (virtualValues != null) {
2282 for (int i = 0; i < virtualValues.size(); i++) {
2283 final View parent = virtualValues.keyAt(i);
2284 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
2285 parent.autofill(childrenValues);
2286 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07002287 // TODO: we should provide a callback so the parent can call failures; something
2288 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002289 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002290 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002291
Felipe Leme11166522018-05-07 10:18:47 -07002292 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07002293 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07002294 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002295 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002296 }
2297
Felipe Leme11166522018-05-07 10:18:47 -07002298 private LogMaker newLog(int category) {
Felipe Lemeb838a092018-05-22 14:56:15 -07002299 final LogMaker log = new LogMaker(category)
Felipe Leme833c99b2018-05-24 10:41:48 -07002300 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
2301
2302 if (isCompatibilityModeEnabledLocked()) {
2303 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
2304 }
Felipe Lemeb838a092018-05-22 14:56:15 -07002305 final AutofillClient client = getClient();
2306 if (client == null) {
2307 // Client should never be null here, but it doesn't hurt to check...
2308 log.setPackageName(mContext.getPackageName());
2309 } else {
2310 log.setComponentName(client.autofillClientGetComponentName());
2311 }
2312 return log;
Felipe Leme11166522018-05-07 10:18:47 -07002313 }
2314
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002315 /**
2316 * Set the tracked views.
2317 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002318 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002319 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002320 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07002321 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002322 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002323 */
Felipe Leme27e20222017-05-18 15:24:11 -07002324 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002325 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
2326 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Felipe Leme785777b2019-04-30 18:12:50 -07002327 if (saveTriggerId != null) {
2328 saveTriggerId.resetSessionId();
2329 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002330 synchronized (mLock) {
Felipe Leme159cb002019-03-27 10:44:59 -07002331 if (sVerbose) {
2332 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
2333 + ", trackedIds=" + Arrays.toString(trackedIds)
2334 + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible
2335 + ", saveOnFinish=" + saveOnFinish
2336 + ", fillableIds=" + Arrays.toString(fillableIds)
2337 + ", saveTrigerId=" + saveTriggerId
2338 + ", mFillableIds=" + mFillableIds
2339 + ", mEnabled=" + mEnabled
2340 + ", mSessionId=" + mSessionId);
2341
2342 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002343 if (mEnabled && mSessionId == sessionId) {
2344 if (saveOnAllViewsInvisible) {
2345 mTrackedViews = new TrackedViews(trackedIds);
2346 } else {
2347 mTrackedViews = null;
2348 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002349 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07002350 if (fillableIds != null) {
2351 if (mFillableIds == null) {
2352 mFillableIds = new ArraySet<>(fillableIds.length);
2353 }
2354 for (AutofillId id : fillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002355 id.resetSessionId();
Felipe Leme27e20222017-05-18 15:24:11 -07002356 mFillableIds.add(id);
2357 }
Felipe Leme27e20222017-05-18 15:24:11 -07002358 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002359
2360 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
2361 // Turn off trigger on previous view id.
2362 setNotifyOnClickLocked(mSaveTriggerId, false);
2363 }
2364
2365 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
2366 // Turn on trigger on new view id.
2367 mSaveTriggerId = saveTriggerId;
2368 setNotifyOnClickLocked(mSaveTriggerId, true);
2369 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002370 }
2371 }
2372 }
2373
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002374 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
2375 final View view = findView(id);
2376 if (view == null) {
2377 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
2378 return;
2379 }
2380 view.setNotifyAutofillManagerOnClick(notify);
2381 }
2382
Felipe Lemec24a56a2017-08-03 14:27:57 -07002383 private void setSaveUiState(int sessionId, boolean shown) {
2384 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
2385 synchronized (mLock) {
2386 if (mSessionId != NO_SESSION) {
2387 // Race condition: app triggered a new session after the previous session was
2388 // finished but before server called setSaveUiState() - need to cancel the new
2389 // session to avoid further inconsistent behavior.
2390 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
2391 + ") called on existing session " + mSessionId + "; cancelling it");
2392 cancelSessionLocked();
2393 }
2394 if (shown) {
2395 mSessionId = sessionId;
2396 mState = STATE_SHOWING_SAVE_UI;
2397 } else {
2398 mSessionId = NO_SESSION;
2399 mState = STATE_UNKNOWN;
2400 }
2401 }
2402 }
2403
Felipe Leme650f7ab2017-09-19 13:08:24 -07002404 /**
2405 * Marks the state of the session as finished.
2406 *
2407 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002408 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2409 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
Felipe Lemed9dc9542018-09-19 11:54:28 -07002410 * changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
2411 * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
2412 * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
2413 * service disabled further autofill requests for the activity).
Felipe Leme6e43dd32019-03-13 17:25:33 -07002414 * @param autofillableIds list of ids that could trigger autofill, use to not handle a new
2415 * session when they're entered.
Felipe Leme650f7ab2017-09-19 13:08:24 -07002416 */
Felipe Leme6e43dd32019-03-13 17:25:33 -07002417 private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002418 if (autofillableIds != null) {
2419 for (int i = 0; i < autofillableIds.size(); i++) {
2420 autofillableIds.get(i).resetSessionId();
2421 }
2422 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002423 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002424 if (sVerbose) {
2425 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
Felipe Leme6e43dd32019-03-13 17:25:33 -07002426 + getStateAsString(newState) + "; autofillableIds=" + autofillableIds);
2427 }
2428 if (autofillableIds != null) {
2429 mEnteredIds = new ArraySet<>(autofillableIds);
Felipe Leme0c8ce322018-03-23 13:54:22 -07002430 }
Felipe Lemed9dc9542018-09-19 11:54:28 -07002431 if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002432 resetSessionLocked(/* resetEnteredIds= */ true);
2433 mState = STATE_UNKNOWN;
2434 } else {
2435 resetSessionLocked(/* resetEnteredIds= */ false);
2436 mState = newState;
2437 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002438 }
2439 }
2440
Felipe Leme284ad1c2018-11-15 18:16:12 -08002441 /**
2442 * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
2443 *
2444 * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
2445 * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
2446 */
2447 private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
2448 synchronized (mLock) {
2449 if (mAugmentedAutofillServiceClient == null) {
2450 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
2451 }
2452 final Bundle resultData = new Bundle();
2453 resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
2454 mAugmentedAutofillServiceClient.asBinder());
2455
2456 try {
2457 result.send(0, resultData);
2458 } catch (RemoteException e) {
2459 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
2460 }
2461 }
2462 }
2463
Feng Cao43c20432020-03-26 17:57:34 -07002464 private void requestShowSoftInput(@NonNull AutofillId id) {
2465 if (sVerbose) Log.v(TAG, "requestShowSoftInput(" + id + ")");
2466 final AutofillClient client = getClient();
2467 if (client == null) {
2468 return;
2469 }
2470 final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
2471 if (view == null) {
2472 if (sVerbose) Log.v(TAG, "View is not found");
2473 return;
2474 }
2475 final Handler handler = view.getHandler();
2476 if (handler == null) {
2477 if (sVerbose) Log.v(TAG, "Ignoring requestShowSoftInput due to no handler in view");
2478 return;
2479 }
2480 if (handler.getLooper() != Looper.myLooper()) {
2481 // The view is running on a different thread than our own, so we need to reschedule
2482 // our work for over there.
2483 if (sVerbose) Log.v(TAG, "Scheduling showSoftInput() on the view UI thread");
2484 handler.post(() -> requestShowSoftInputInViewThread(view));
2485 } else {
2486 requestShowSoftInputInViewThread(view);
2487 }
2488 }
2489
2490 // This method must be called from within the View thread.
2491 private static void requestShowSoftInputInViewThread(@NonNull View view) {
2492 if (!view.isFocused()) {
2493 Log.w(TAG, "Ignoring requestShowSoftInput() due to non-focused view");
2494 return;
2495 }
2496 final InputMethodManager inputMethodManager = view.getContext().getSystemService(
2497 InputMethodManager.class);
2498 boolean ret = inputMethodManager.showSoftInput(view, /*flags=*/ 0);
2499 if (sVerbose) Log.v(TAG, " InputMethodManager.showSoftInput returns " + ret);
2500 }
2501
Dake Gub0fa3782018-02-26 12:25:14 -08002502 /** @hide */
2503 public void requestHideFillUi() {
2504 requestHideFillUi(mIdShownFillUi, true);
2505 }
2506
2507 private void requestHideFillUi(AutofillId id, boolean force) {
2508 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002509 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002510 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002511 if (force) {
2512 // When user taps outside autofill window, force to close fill ui even id does
2513 // not match.
2514 AutofillClient client = getClient();
2515 if (client != null) {
2516 client.autofillClientRequestHideFillUi();
2517 }
2518 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002519 return;
2520 }
Felipe Leme27e20222017-05-18 15:24:11 -07002521 requestHideFillUi(id, anchor);
2522 }
2523
2524 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002525
2526 AutofillCallback callback = null;
2527 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002528 // We do not check the session id for two reasons:
2529 // 1. If local and remote session id are off sync the UI would be stuck shown
2530 // 2. There is a race between the user state being destroyed due the fill
2531 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002532 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002533 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002534 if (client.autofillClientRequestHideFillUi()) {
2535 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002536 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002537 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002538 }
2539 }
2540
2541 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002542 if (id.isVirtualInt()) {
2543 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002544 AutofillCallback.EVENT_INPUT_HIDDEN);
2545 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002546 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002547 }
2548 }
2549 }
2550
Joanne Chung9e247b12019-07-19 23:41:32 +08002551 private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
2552 synchronized (mLock) {
2553 if (mOptions == null) {
2554 return;
2555 }
2556 long expiration = SystemClock.elapsedRealtime() + disableDuration;
2557 // Protect it against overflow
2558 if (expiration < 0) {
2559 expiration = Long.MAX_VALUE;
2560 }
2561 if (componentName != null) {
2562 if (mOptions.disabledActivities == null) {
2563 mOptions.disabledActivities = new ArrayMap<>();
2564 }
2565 mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
2566 } else {
2567 mOptions.appDisabledExpiration = expiration;
2568 }
2569 }
2570 }
2571
2572 void notifyReenableAutofill() {
2573 synchronized (mLock) {
2574 if (mOptions == null) {
2575 return;
2576 }
2577 mOptions.appDisabledExpiration = 0;
2578 mOptions.disabledActivities = null;
2579 }
2580 }
2581
Felipe Leme17292d12017-10-24 14:03:10 -07002582 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002583 if (sVerbose) {
2584 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002585 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002586 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002587 final View anchor = findView(id);
2588 if (anchor == null) {
2589 return;
2590 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002591
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002592 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002593 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002594 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002595 callback = mCallback;
2596 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002597 }
2598
2599 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002600 if (id.isVirtualInt()) {
2601 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002602 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2603 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002604 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002605 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002606 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002607
Felipe Lemee5db59d2019-05-07 11:10:07 -07002608 if (sessionFinishedState != STATE_UNKNOWN) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002609 // Callback call was "hijacked" to also update the session state.
Felipe Leme6e43dd32019-03-13 17:25:33 -07002610 setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
Felipe Leme4753bb02017-03-22 20:24:00 -07002611 }
2612 }
2613
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002614 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002615 * Find a single view by its id.
2616 *
2617 * @param autofillId The autofill id of the view
2618 *
2619 * @return The view or {@code null} if view was not found
2620 */
2621 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002622 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002623 if (client != null) {
2624 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002625 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002626 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002627 }
2628
Felipe Lemebb810922017-04-25 15:54:06 -07002629 /** @hide */
2630 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002631 return mService != null;
2632 }
2633
Felipe Lemec24a56a2017-08-03 14:27:57 -07002634 /** @hide */
2635 public void onPendingSaveUi(int operation, IBinder token) {
2636 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2637
2638 synchronized (mLock) {
2639 try {
2640 mService.onPendingSaveUi(operation, token);
2641 } catch (RemoteException e) {
2642 e.rethrowFromSystemServer();
2643 }
2644 }
2645 }
2646
2647 /** @hide */
2648 public void dump(String outerPrefix, PrintWriter pw) {
2649 pw.print(outerPrefix); pw.println("AutofillManager:");
2650 final String pfx = outerPrefix + " ";
2651 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002652 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002653 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Lemeb546ca72018-08-15 08:44:12 -07002654 final AutofillClient client = getClient();
2655 if (client != null) {
2656 pw.print(pfx); pw.print("client: "); pw.print(client);
2657 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2658 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002659 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
Felipe Lemedf30f272019-03-19 13:47:48 -07002660 pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002661 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2662 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002663 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002664 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
Felipe Lemef5e26302018-06-08 16:26:39 -07002665 pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002666 pw.print(pfx); pw.print("tracked views: ");
2667 if (mTrackedViews == null) {
2668 pw.println("null");
2669 } else {
2670 final String pfx2 = pfx + " ";
2671 pw.println();
2672 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2673 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2674 }
2675 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002676 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Lemea7de4022019-02-19 17:16:45 -08002677 if (mEnteredForAugmentedAutofillIds != null) {
2678 pw.print(pfx); pw.print("entered ids for augmented autofill: ");
2679 pw.println(mEnteredForAugmentedAutofillIds);
2680 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07002681 if (mForAugmentedAutofillOnly) {
2682 pw.print(pfx); pw.println("For Augmented Autofill Only");
2683 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002684 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2685 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Lemea4f39cd2019-02-19 15:08:59 -08002686 if (mOptions != null) {
2687 pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
2688 }
Felipe Lemeb546ca72018-08-15 08:44:12 -07002689 pw.print(pfx); pw.print("compat mode enabled: ");
2690 synchronized (mLock) {
2691 if (mCompatibilityBridge != null) {
2692 final String pfx2 = pfx + " ";
2693 pw.println("true");
2694 pw.print(pfx2); pw.print("windowId: ");
2695 pw.println(mCompatibilityBridge.mFocusedWindowId);
2696 pw.print(pfx2); pw.print("nodeId: ");
2697 pw.println(mCompatibilityBridge.mFocusedNodeId);
2698 pw.print(pfx2); pw.print("virtualId: ");
2699 pw.println(AccessibilityNodeInfo
2700 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2701 pw.print(pfx2); pw.print("focusedBounds: ");
2702 pw.println(mCompatibilityBridge.mFocusedBounds);
2703 } else {
2704 pw.println("false");
2705 }
2706 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002707 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2708 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002709 }
2710
Andreas Gampe3f24e692018-02-05 13:24:28 -08002711 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002712 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002713 return getStateAsString(mState);
2714 }
2715
2716 @NonNull
2717 private static String getStateAsString(int state) {
2718 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002719 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002720 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002721 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002722 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002723 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002724 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002725 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002726 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002727 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002728 return "DISABLED_BY_SERVICE";
2729 case STATE_UNKNOWN_COMPAT_MODE:
2730 return "UNKNOWN_COMPAT_MODE";
Felipe Lemed9dc9542018-09-19 11:54:28 -07002731 case STATE_UNKNOWN_FAILED:
2732 return "UNKNOWN_FAILED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002733 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002734 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002735 }
2736 }
2737
Felipe Leme559e21d2019-01-18 17:57:21 -08002738 /** @hide */
2739 public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
Felipe Lemecc510272019-02-07 14:56:27 -08002740 switch (flags) {
2741 case FLAG_SMART_SUGGESTION_OFF:
2742 return "OFF";
2743 case FLAG_SMART_SUGGESTION_SYSTEM:
2744 return "SYSTEM";
2745 default:
2746 return "INVALID:" + flags;
2747 }
Felipe Leme559e21d2019-01-18 17:57:21 -08002748 }
2749
Andreas Gampe3f24e692018-02-05 13:24:28 -08002750 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002751 private boolean isActiveLocked() {
2752 return mState == STATE_ACTIVE;
2753 }
2754
Andreas Gampe3f24e692018-02-05 13:24:28 -08002755 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002756 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002757 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002758 }
2759
Andreas Gampe3f24e692018-02-05 13:24:28 -08002760 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002761 private boolean isFinishedLocked() {
2762 return mState == STATE_FINISHED;
2763 }
2764
Felipe Leme9876a6f2017-05-30 15:47:28 -07002765 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002766 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002767 if (client == null) {
2768 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2769 return;
2770 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002771 client.autofillClientRunOnUiThread(runnable);
2772 }
2773
2774 /**
2775 * Implementation of the accessibility based compatibility.
2776 */
2777 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2778 @GuardedBy("mLock")
2779 private final Rect mFocusedBounds = new Rect();
2780 @GuardedBy("mLock")
2781 private final Rect mTempBounds = new Rect();
2782
2783 @GuardedBy("mLock")
2784 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2785 @GuardedBy("mLock")
2786 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2787
2788 // Need to report a fake service in case a11y clients check the service list
2789 @NonNull
2790 @GuardedBy("mLock")
2791 AccessibilityServiceInfo mCompatServiceInfo;
2792
2793 CompatibilityBridge() {
2794 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2795 am.setAccessibilityPolicy(this);
2796 }
2797
2798 private AccessibilityServiceInfo getCompatServiceInfo() {
2799 synchronized (mLock) {
2800 if (mCompatServiceInfo != null) {
2801 return mCompatServiceInfo;
2802 }
2803 final Intent intent = new Intent();
2804 intent.setComponent(new ComponentName("android",
2805 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2806 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2807 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2808 try {
2809 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2810 } catch (XmlPullParserException | IOException e) {
2811 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2812 throw new IllegalStateException("Cannot find compat autofill service");
2813 }
2814 return mCompatServiceInfo;
2815 }
2816 }
2817
2818 @Override
2819 public boolean isEnabled(boolean accessibilityEnabled) {
2820 return true;
2821 }
2822
2823 @Override
2824 public int getRelevantEventTypes(int relevantEventTypes) {
2825 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2826 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002827 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002828 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2829 }
2830
2831 @Override
2832 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2833 List<AccessibilityServiceInfo> installedServices) {
2834 if (installedServices == null) {
2835 installedServices = new ArrayList<>();
2836 }
2837 installedServices.add(getCompatServiceInfo());
2838 return installedServices;
2839 }
2840
2841 @Override
2842 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2843 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2844 if (enabledService == null) {
2845 enabledService = new ArrayList<>();
2846 }
2847 enabledService.add(getCompatServiceInfo());
2848 return enabledService;
2849 }
2850
2851 @Override
2852 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2853 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002854 final int type = event.getEventType();
2855 if (sVerbose) {
2856 // NOTE: this is waaay spammy, but that's life.
2857 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme7d4461d2018-08-16 10:49:03 -07002858 + "): virtualId="
2859 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2860 + ", client=" + getClient());
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002861 }
2862 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002863 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2864 synchronized (mLock) {
2865 if (mFocusedWindowId == event.getWindowId()
2866 && mFocusedNodeId == event.getSourceNodeId()) {
2867 return event;
2868 }
2869 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2870 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2871 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2872 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2873 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2874 mFocusedBounds.set(0, 0, 0, 0);
2875 }
2876 final int windowId = event.getWindowId();
2877 final long nodeId = event.getSourceNodeId();
2878 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2879 mFocusedWindowId = windowId;
2880 mFocusedNodeId = nodeId;
2881 }
2882 }
2883 } break;
2884
2885 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2886 synchronized (mLock) {
2887 if (mFocusedWindowId == event.getWindowId()
2888 && mFocusedNodeId == event.getSourceNodeId()) {
2889 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2890 }
2891 }
2892 } break;
2893
Felipe Leme54cc6832018-03-06 12:54:31 -08002894 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2895 synchronized (mLock) {
2896 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2897 }
2898 } break;
2899
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002900 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2901 final AutofillClient client = getClient();
2902 if (client != null) {
2903 synchronized (mLock) {
2904 if (client.autofillClientIsFillUiShowing()) {
2905 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2906 }
2907 updateTrackedViewsLocked();
2908 }
2909 }
2910 } break;
2911 }
2912
2913 return accessibilityEnabled ? event : null;
2914 }
2915
2916 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2917 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2918 if (!isVirtualNode(virtualId)) {
2919 return false;
2920 }
2921 final View view = findViewByAccessibilityId(windowId, nodeId);
2922 if (view == null) {
2923 return false;
2924 }
2925 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2926 if (node == null) {
2927 return false;
2928 }
2929 if (!node.isEditable()) {
2930 return false;
2931 }
2932 final Rect newBounds = mTempBounds;
2933 node.getBoundsInScreen(newBounds);
2934 if (newBounds.equals(focusedBounds)) {
2935 return false;
2936 }
2937 focusedBounds.set(newBounds);
2938 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2939 return true;
2940 }
2941
2942 private void notifyViewExited(int windowId, long nodeId) {
2943 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2944 if (!isVirtualNode(virtualId)) {
2945 return;
2946 }
2947 final View view = findViewByAccessibilityId(windowId, nodeId);
2948 if (view == null) {
2949 return;
2950 }
2951 AutofillManager.this.notifyViewExited(view, virtualId);
2952 }
2953
2954 private void notifyValueChanged(int windowId, long nodeId) {
2955 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2956 if (!isVirtualNode(virtualId)) {
2957 return;
2958 }
2959 final View view = findViewByAccessibilityId(windowId, nodeId);
2960 if (view == null) {
2961 return;
2962 }
2963 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2964 if (node == null) {
2965 return;
2966 }
2967 AutofillManager.this.notifyValueChanged(view, virtualId,
2968 AutofillValue.forText(node.getText()));
2969 }
2970
Felipe Leme54cc6832018-03-06 12:54:31 -08002971 private void notifyViewClicked(int windowId, long nodeId) {
2972 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2973 if (!isVirtualNode(virtualId)) {
2974 return;
2975 }
2976 final View view = findViewByAccessibilityId(windowId, nodeId);
2977 if (view == null) {
2978 return;
2979 }
2980 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2981 if (node == null) {
2982 return;
2983 }
2984 AutofillManager.this.notifyViewClicked(view, virtualId);
2985 }
2986
Andreas Gampe3f24e692018-02-05 13:24:28 -08002987 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002988 private void updateTrackedViewsLocked() {
2989 if (mTrackedViews != null) {
2990 mTrackedViews.onVisibleForAutofillChangedLocked();
2991 }
2992 }
2993
2994 private View findViewByAccessibilityId(int windowId, long nodeId) {
2995 final AutofillClient client = getClient();
2996 if (client == null) {
2997 return null;
2998 }
2999 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
3000 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
3001 }
3002
3003 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
3004 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
3005 if (provider == null) {
3006 return null;
3007 }
3008 return provider.createAccessibilityNodeInfo(virtualId);
3009 }
3010
3011 private boolean isVirtualNode(int nodeId) {
3012 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
3013 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
3014 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07003015 }
3016
Felipe Lemee6010f22017-03-03 11:19:51 -08003017 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003018 * View tracking information. Once all tracked views become invisible the session is finished.
3019 */
3020 private class TrackedViews {
3021 /** Visible tracked views */
3022 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
3023
3024 /** Invisible tracked views */
3025 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
3026
3027 /**
3028 * Check if set is null or value is in set.
3029 *
3030 * @param set The set or null (== empty set)
3031 * @param value The value that might be in the set
3032 *
3033 * @return {@code true} iff set is not empty and value is in set
3034 */
Felipe Leme27e20222017-05-18 15:24:11 -07003035 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003036 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
3037 return set != null && set.contains(value);
3038 }
3039
3040 /**
3041 * Add a value to a set. If set is null, create a new set.
3042 *
3043 * @param set The set or null (== empty set)
3044 * @param valueToAdd The value to add
3045 *
3046 * @return The set including the new value. If set was {@code null}, a set containing only
3047 * the new value.
3048 */
Felipe Leme27e20222017-05-18 15:24:11 -07003049 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003050 @NonNull
3051 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
3052 if (set == null) {
3053 set = new ArraySet<>(1);
3054 }
3055
3056 set.add(valueToAdd);
3057
3058 return set;
3059 }
3060
3061 /**
3062 * Remove a value from a set.
3063 *
3064 * @param set The set or null (== empty set)
3065 * @param valueToRemove The value to remove
3066 *
3067 * @return The set without the removed value. {@code null} if set was null, or is empty
3068 * after removal.
3069 */
Felipe Leme27e20222017-05-18 15:24:11 -07003070 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003071 @Nullable
3072 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
3073 if (set == null) {
3074 return null;
3075 }
3076
3077 set.remove(valueToRemove);
3078
3079 if (set.isEmpty()) {
3080 return null;
3081 }
3082
3083 return set;
3084 }
3085
3086 /**
3087 * Set the tracked views.
3088 *
3089 * @param trackedIds The views to be tracked
3090 */
Felipe Leme27e20222017-05-18 15:24:11 -07003091 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08003092 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003093 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003094 final boolean[] isVisible;
3095
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003096 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003097 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003098 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003099 } else {
3100 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07003101 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003102 }
3103
Felipe Leme27e20222017-05-18 15:24:11 -07003104 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003105 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07003106 final AutofillId id = trackedIds[i];
Felipe Leme785777b2019-04-30 18:12:50 -07003107 id.resetSessionId();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003108
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003109 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07003110 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003111 } else {
3112 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3113 }
3114 }
3115 }
3116
Felipe Leme9f9ee252017-04-27 13:56:22 -07003117 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003118 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003119 + " mVisibleTrackedIds=" + mVisibleTrackedIds
3120 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
3121 }
3122
3123 if (mVisibleTrackedIds == null) {
3124 finishSessionLocked();
3125 }
3126 }
3127
3128 /**
3129 * Called when a {@link View view's} visibility changes.
3130 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07003131 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003132 * @param isVisible visible if the view is visible in the view hierarchy.
3133 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003134 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08003135 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07003136 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003137 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003138 + isVisible);
3139 }
3140
Dake Gu67decfa2017-12-27 11:48:08 -08003141 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003142 if (isVisible) {
3143 if (isInSet(mInvisibleTrackedIds, id)) {
3144 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
3145 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
3146 }
3147 } else {
3148 if (isInSet(mVisibleTrackedIds, id)) {
3149 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
3150 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3151 }
3152 }
3153 }
3154
3155 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07003156 if (sVerbose) {
3157 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
3158 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003159 finishSessionLocked();
3160 }
3161 }
3162
3163 /**
3164 * Called once the client becomes visible.
3165 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003166 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003167 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003168 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003169 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003170 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003171 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08003172 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003173 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
3174 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
3175 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07003176 if (sVerbose) {
3177 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
3178 + " vis=" + mVisibleTrackedIds);
3179 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003180 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003181 final ArrayList<AutofillId> orderedInvisibleIds =
3182 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003183 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3184 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003185
3186 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
3187 for (int i = 0; i < numInvisibleTrackedIds; i++) {
3188 final AutofillId id = orderedInvisibleIds.get(i);
3189 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003190 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3191
Felipe Leme9f9ee252017-04-27 13:56:22 -07003192 if (sDebug) {
3193 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003194 }
3195 } else {
3196 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3197 }
3198 }
3199 }
3200
3201 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003202 final ArrayList<AutofillId> orderedVisibleIds =
3203 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003204 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3205 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003206
3207 final int numVisibleTrackedIds = orderedVisibleIds.size();
3208 for (int i = 0; i < numVisibleTrackedIds; i++) {
3209 final AutofillId id = orderedVisibleIds.get(i);
3210
3211 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003212 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3213 } else {
3214 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3215
Felipe Leme9f9ee252017-04-27 13:56:22 -07003216 if (sDebug) {
3217 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003218 }
3219 }
3220 }
3221 }
3222
3223 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
3224 mVisibleTrackedIds = updatedVisibleTrackedIds;
3225 }
3226
3227 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003228 if (sVerbose) {
3229 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
3230 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003231 finishSessionLocked();
3232 }
3233 }
3234 }
3235
3236 /**
Felipe Leme744976e2017-07-31 11:34:14 -07003237 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08003238 *
3239 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003240 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003241 */
3242 public abstract static class AutofillCallback {
3243
3244 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07003245 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
3246 EVENT_INPUT_SHOWN,
3247 EVENT_INPUT_HIDDEN,
3248 EVENT_INPUT_UNAVAILABLE
3249 })
Felipe Lemee6010f22017-03-03 11:19:51 -08003250 @Retention(RetentionPolicy.SOURCE)
3251 public @interface AutofillEventType {}
3252
3253 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003254 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08003255 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003256 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08003257 * should be hidden upon receiving this event.
3258 */
3259 public static final int EVENT_INPUT_SHOWN = 1;
3260
3261 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003262 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003263 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003264 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08003265 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
3266 */
3267 public static final int EVENT_INPUT_HIDDEN = 2;
3268
3269 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003270 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07003271 * autofill is not available.
3272 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003273 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07003274 * to avoid flickering, it could shown it upon receiving this event.
3275 */
3276 public static final int EVENT_INPUT_UNAVAILABLE = 3;
3277
3278 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08003279 * Called after a change in the autofill state associated with a view.
3280 *
3281 * @param view view associated with the change.
3282 *
3283 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3284 */
Felipe Leme81f01d92017-03-16 17:13:25 -07003285 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
3286 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003287
3288 /**
3289 * Called after a change in the autofill state associated with a virtual view.
3290 *
3291 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07003292 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08003293 *
3294 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3295 */
Felipe Leme6dcec872017-05-25 11:24:23 -07003296 public void onAutofillEvent(@NonNull View view, int virtualId,
3297 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07003298 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003299 }
3300
Felipe Leme640f30a2017-03-06 15:44:06 -08003301 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
3302 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00003303
Felipe Leme284ad1c2018-11-15 18:16:12 -08003304 private AutofillManagerClient(AutofillManager autofillManager) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003305 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00003306 }
3307
3308 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07003309 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003310 final AutofillManager afm = mAfm.get();
3311 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07003312 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00003313 }
3314 }
3315
3316 @Override
Adam He5061db82020-03-11 15:58:27 -07003317 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3318 boolean hideHighlight) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003319 final AutofillManager afm = mAfm.get();
3320 if (afm != null) {
Adam He5061db82020-03-11 15:58:27 -07003321 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
Svet Ganov782043c2017-02-11 00:52:02 +00003322 }
3323 }
3324
3325 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07003326 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08003327 Intent fillInIntent, boolean authenticateInline) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003328 final AutofillManager afm = mAfm.get();
3329 if (afm != null) {
Adam He5be0f152020-02-03 15:53:32 -08003330 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
3331 authenticateInline));
Svet Ganov782043c2017-02-11 00:52:02 +00003332 }
3333 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003334
3335 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003336 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3337 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003338 final AutofillManager afm = mAfm.get();
3339 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003340 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3341 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07003342 }
3343 }
3344
3345 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003346 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003347 final AutofillManager afm = mAfm.get();
3348 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08003349 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07003350 }
3351 }
3352
3353 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07003354 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003355 final AutofillManager afm = mAfm.get();
3356 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07003357 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08003358 }
3359 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07003360
3361 @Override
Joanne Chung9e247b12019-07-19 23:41:32 +08003362 public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
3363 throws RemoteException {
3364 final AutofillManager afm = mAfm.get();
3365 if (afm != null) {
3366 afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
3367 }
3368 }
3369
3370 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08003371 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
3372 final AutofillManager afm = mAfm.get();
3373 if (afm != null) {
3374 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
3375 }
3376 }
3377
3378 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07003379 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003380 final AutofillManager afm = mAfm.get();
3381 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003382 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003383 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07003384 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07003385 } catch (IntentSender.SendIntentException e) {
3386 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
3387 }
3388 });
3389 }
3390 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003391
3392 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07003393 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003394 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
3395 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003396 final AutofillManager afm = mAfm.get();
3397 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003398 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
3399 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003400 }
3401 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07003402
3403 @Override
3404 public void setSaveUiState(int sessionId, boolean shown) {
3405 final AutofillManager afm = mAfm.get();
3406 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003407 afm.post(() -> afm.setSaveUiState(sessionId, shown));
3408 }
3409 }
3410
3411 @Override
Felipe Leme6e43dd32019-03-13 17:25:33 -07003412 public void setSessionFinished(int newState, List<AutofillId> autofillableIds) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003413 final AutofillManager afm = mAfm.get();
3414 if (afm != null) {
Felipe Leme6e43dd32019-03-13 17:25:33 -07003415 afm.post(() -> afm.setSessionFinished(newState, autofillableIds));
Felipe Lemec24a56a2017-08-03 14:27:57 -07003416 }
3417 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003418
3419 @Override
3420 public void getAugmentedAutofillClient(IResultReceiver result) {
3421 final AutofillManager afm = mAfm.get();
3422 if (afm != null) {
3423 afm.post(() -> afm.getAugmentedAutofillClient(result));
3424 }
3425 }
Feng Cao43c20432020-03-26 17:57:34 -07003426
3427 @Override
3428 public void requestShowSoftInput(@NonNull AutofillId id) {
3429 final AutofillManager afm = mAfm.get();
3430 if (afm != null) {
3431 afm.post(() -> afm.requestShowSoftInput(id));
3432 }
3433 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003434 }
3435
3436 private static final class AugmentedAutofillManagerClient
3437 extends IAugmentedAutofillManagerClient.Stub {
3438 private final WeakReference<AutofillManager> mAfm;
3439
3440 private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
3441 mAfm = new WeakReference<>(autofillManager);
3442 }
3443
3444 @Override
3445 public Rect getViewCoordinates(@NonNull AutofillId id) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003446 final AutofillManager afm = mAfm.get();
3447 if (afm == null) return null;
3448
Feng Cao0fd56412020-02-13 19:58:30 -08003449 final View view = getView(afm, id);
Adam He234030a2019-03-15 15:57:57 -07003450 if (view == null) {
Adam He234030a2019-03-15 15:57:57 -07003451 return null;
3452 }
Feng Cao023b84c2018-12-14 15:51:17 -08003453 final Rect windowVisibleDisplayFrame = new Rect();
3454 view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
Felipe Leme284ad1c2018-11-15 18:16:12 -08003455 final int[] location = new int[2];
3456 view.getLocationOnScreen(location);
Feng Cao023b84c2018-12-14 15:51:17 -08003457 final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
3458 location[0] + view.getWidth(),
3459 location[1] - windowVisibleDisplayFrame.top + view.getHeight());
Felipe Leme284ad1c2018-11-15 18:16:12 -08003460 if (sVerbose) {
3461 Log.v(TAG, "Coordinates for " + id + ": " + rect);
3462 }
3463 return rect;
3464 }
3465
3466 @Override
Adam He5061db82020-03-11 15:58:27 -07003467 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3468 boolean hideHighlight) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003469 final AutofillManager afm = mAfm.get();
3470 if (afm != null) {
Adam He5061db82020-03-11 15:58:27 -07003471 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
Felipe Leme284ad1c2018-11-15 18:16:12 -08003472 }
3473 }
Feng Cao158b6562019-01-07 15:42:52 -08003474
3475 @Override
3476 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3477 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3478 final AutofillManager afm = mAfm.get();
3479 if (afm != null) {
3480 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3481 presenter));
3482 }
3483 }
3484
3485 @Override
3486 public void requestHideFillUi(int sessionId, AutofillId id) {
3487 final AutofillManager afm = mAfm.get();
3488 if (afm != null) {
3489 afm.post(() -> afm.requestHideFillUi(id, false));
3490 }
3491 }
Feng Cao0fd56412020-02-13 19:58:30 -08003492
3493 @Override
3494 public boolean requestAutofill(int sessionId, AutofillId id) {
3495 final AutofillManager afm = mAfm.get();
3496 if (afm == null || afm.mSessionId != sessionId) {
3497 if (sDebug) {
3498 Slog.d(TAG, "Autofill not available or sessionId doesn't match");
3499 }
3500 return false;
3501 }
3502 final View view = getView(afm, id);
3503 if (view == null || !view.isFocused()) {
3504 if (sDebug) {
3505 Slog.d(TAG, "View not available or is not on focus");
3506 }
3507 return false;
3508 }
3509 if (sVerbose) {
3510 Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
3511 }
Feng Cao08abd462020-04-20 17:47:27 -07003512 afm.post(() -> afm.requestAutofillFromNewSession(view));
Feng Cao0fd56412020-02-13 19:58:30 -08003513 return true;
3514 }
3515
3516 @Nullable
3517 private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
3518 final AutofillClient client = afm.getClient();
3519 if (client == null) {
3520 Log.w(TAG, "getView(" + id + "): no autofill client");
3521 return null;
3522 }
3523 View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3524 if (view == null) {
3525 Log.w(TAG, "getView(" + id + "): could not find view");
3526 }
3527 return view;
3528 }
Svet Ganov782043c2017-02-11 00:52:02 +00003529 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08003530}