blob: 83a79344917c2d014a7def7e18357c5af81c638f [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 /**
886 * Explicitly requests a new autofill context for virtual views.
887 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700888 * <p>Normally, the autofill context is automatically started if necessary when
889 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
890 * cases where it must be explicitly started. For example, when the virtual view offers an
891 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700892 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700893 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
894 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
895 * the absolute bounds could be calculated by:
896 *
897 * <pre class="prettyprint">
898 * int offset[] = new int[2];
899 * getLocationOnScreen(offset);
900 * Rect absBounds = new Rect(bounds.left + offset[0],
901 * bounds.top + offset[1],
902 * bounds.right + offset[0], bounds.bottom + offset[1]);
903 * </pre>
904 *
905 * @param view the virtual view parent.
906 * @param virtualId id identifying the virtual child inside the parent view.
907 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700908 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700909 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
910 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700911 }
912
Felipe Leme2ac463e2017-03-13 14:06:25 -0700913 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700914 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800915 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700916 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800917 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700918 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700919 notifyViewEntered(view, 0);
920 }
921
Andreas Gampe3f24e692018-02-05 13:24:28 -0800922 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800923 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800924 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700925 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800926 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
927 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700928 }
929 return true;
930 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800931 if (isFinishedLocked()) {
932 // Session already finished: ignore if automatic request and view already entered
933 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
934 && mEnteredIds.contains(id)) {
935 if (sVerbose) {
936 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
937 + ") on state " + getStateAsStringLocked()
938 + " because view was already entered: " + mEnteredIds);
939 }
940 return true;
941 }
942 }
Felipe Leme17292d12017-10-24 14:03:10 -0700943 return false;
944 }
945
Dake Gu67decfa2017-12-27 11:48:08 -0800946 private boolean isClientVisibleForAutofillLocked() {
947 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800948 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800949 }
950
951 private boolean isClientDisablingEnterExitEvent() {
952 final AutofillClient client = getClient();
953 return client != null && client.isDisablingEnterExitEventForAutofill();
954 }
955
Felipe Lemed1146422017-04-26 10:17:05 -0700956 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700957 if (!hasAutofillFeature()) {
958 return;
959 }
Dake Gu67decfa2017-12-27 11:48:08 -0800960 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700961 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800962 callback = notifyViewEnteredLocked(view, flags);
963 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700964
Dake Gu67decfa2017-12-27 11:48:08 -0800965 if (callback != null) {
966 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
967 }
968 }
Svet Ganov782043c2017-02-11 00:52:02 +0000969
Dake Gu67decfa2017-12-27 11:48:08 -0800970 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800971 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800972 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme42b97932018-02-20 13:04:31 -0800973 final AutofillId id = view.getAutofillId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800974 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800975
976 AutofillCallback callback = null;
977
978 ensureServiceClientAddedIfNeededLocked();
979
Felipe Lemedf30f272019-03-19 13:47:48 -0700980 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemeadb34f52019-03-15 16:24:26 -0700981 if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
982
Dake Gu67decfa2017-12-27 11:48:08 -0800983 if (mCallback != null) {
984 callback = mCallback;
985 }
986 } else {
987 // don't notify entered when Activity is already in background
988 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700989 final AutofillValue value = view.getAutofillValue();
990
Adam Heef0fe2082019-10-25 11:58:15 -0700991 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
992 flags |= FLAG_PASSWORD_INPUT_TYPE;
993 }
994
Felipe Lemec24a56a2017-08-03 14:27:57 -0700995 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700996 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700997 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700998 } else {
999 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -07001000 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1001 if (sDebug) {
1002 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1003 + "mForAugmentedAutofillOnly on manual request");
1004 }
1005 mForAugmentedAutofillOnly = false;
1006 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001007 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001008 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001009 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001010 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001011 }
Dake Gu67decfa2017-12-27 11:48:08 -08001012 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -08001013 }
1014
1015 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001016 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001017 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001018 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001019 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001020 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001021 if (!hasAutofillFeature()) {
1022 return;
1023 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001024 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001025 notifyViewExitedLocked(view);
1026 }
1027 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001028
Andreas Gampe3f24e692018-02-05 13:24:28 -08001029 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001030 void notifyViewExitedLocked(@NonNull View view) {
1031 ensureServiceClientAddedIfNeededLocked();
1032
Felipe Lemedf30f272019-03-19 13:47:48 -07001033 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001034 // dont notify exited when Activity is already in background
1035 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme42b97932018-02-20 13:04:31 -08001036 final AutofillId id = view.getAutofillId();
Philip P. Moltmann44611812017-02-23 12:52:46 -08001037
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001038 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001039 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001040 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001041 }
1042 }
1043
1044 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001045 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001046 *
1047 * @param view {@link View} that was exited.
1048 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001049 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001050 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
1051 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
1052 }
1053
1054 /**
1055 * Called when a virtual view's visibility changed.
1056 *
1057 * @param view {@link View} that was exited.
1058 * @param virtualId id identifying the virtual child inside the parent view.
1059 * @param isVisible visible if the view is visible in the view hierarchy.
1060 */
1061 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
1062 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
1063 }
1064
1065 /**
1066 * Called when a view/virtual view's visibility changed.
1067 *
1068 * @param view {@link View} that was exited.
1069 * @param virtualId id identifying the virtual child inside the parent view.
1070 * @param isVisible visible if the view is visible in the view hierarchy.
1071 * @param virtual Whether the view is virtual.
1072 */
1073 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
1074 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001075 synchronized (mLock) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001076 if (mForAugmentedAutofillOnly) {
1077 if (sVerbose) {
1078 Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
1079 }
1080 return;
1081 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001082 if (mEnabled && isActiveLocked()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001083 final AutofillId id = virtual ? getAutofillId(view, virtualId)
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001084 : view.getAutofillId();
Felipe Leme42b97932018-02-20 13:04:31 -08001085 if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -07001086 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001087 if (mFillableIds.contains(id)) {
1088 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
1089 requestHideFillUi(id, view);
1090 }
1091 }
1092 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001093 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme42b97932018-02-20 13:04:31 -08001094 } else if (sVerbose) {
1095 Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
Felipe Leme27e20222017-05-18 15:24:11 -07001096 }
lpetere28d6b02019-11-27 15:02:42 +08001097 } else if (!virtual && isVisible) {
1098 startAutofillIfNeededLocked(view);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001099 }
1100 }
1101 }
1102
1103 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001104 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001105 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001106 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
1107 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
1108 * the absolute bounds could be calculated by:
1109 *
1110 * <pre class="prettyprint">
1111 * int offset[] = new int[2];
1112 * getLocationOnScreen(offset);
1113 * Rect absBounds = new Rect(bounds.left + offset[0],
1114 * bounds.top + offset[1],
1115 * bounds.right + offset[0], bounds.bottom + offset[1]);
1116 * </pre>
1117 *
1118 * @param view the virtual view parent.
1119 * @param virtualId id identifying the virtual child inside the parent view.
1120 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -08001121 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001122 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
1123 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -07001124 }
1125
Felipe Leme6dcec872017-05-25 11:24:23 -07001126 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -07001127 if (!hasAutofillFeature()) {
1128 return;
1129 }
Dake Gu67decfa2017-12-27 11:48:08 -08001130 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001131 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001132 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
1133 }
Felipe Leme17292d12017-10-24 14:03:10 -07001134
Dake Gu67decfa2017-12-27 11:48:08 -08001135 if (callback != null) {
1136 callback.onAutofillEvent(view, virtualId,
1137 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1138 }
1139 }
Svet Ganov782043c2017-02-11 00:52:02 +00001140
Dake Gu67decfa2017-12-27 11:48:08 -08001141 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -08001142 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001143 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
1144 int flags) {
Felipe Leme305fd402018-09-11 18:20:42 +00001145 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -08001146 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001147 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -08001148
1149 ensureServiceClientAddedIfNeededLocked();
1150
Felipe Lemedf30f272019-03-19 13:47:48 -07001151 if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
Felipe Lemef3b844b2018-10-02 13:50:50 -07001152 if (sVerbose) {
1153 Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
1154 }
Dake Gu67decfa2017-12-27 11:48:08 -08001155 if (mCallback != null) {
1156 callback = mCallback;
1157 }
1158 } else {
1159 // don't notify entered when Activity is already in background
1160 if (!isClientDisablingEnterExitEvent()) {
Adam Heef0fe2082019-10-25 11:58:15 -07001161 if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
1162 flags |= FLAG_PASSWORD_INPUT_TYPE;
1163 }
1164
Felipe Lemec24a56a2017-08-03 14:27:57 -07001165 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001166 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001167 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001168 } else {
1169 // Update focus on existing session.
Felipe Lemee5db59d2019-05-07 11:10:07 -07001170 if (mForAugmentedAutofillOnly && (flags & FLAG_MANUAL_REQUEST) != 0) {
1171 if (sDebug) {
1172 Log.d(TAG, "notifyViewEntered(" + id + "): resetting "
1173 + "mForAugmentedAutofillOnly on manual request");
1174 }
1175 mForAugmentedAutofillOnly = false;
1176 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001177 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001178 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001179 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -07001180 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001181 }
Dake Gu67decfa2017-12-27 11:48:08 -08001182 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -08001183 }
1184
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001185 @GuardedBy("mLock")
1186 private void addEnteredIdLocked(@NonNull AutofillId id) {
1187 if (mEnteredIds == null) {
1188 mEnteredIds = new ArraySet<>(1);
1189 }
Felipe Leme785777b2019-04-30 18:12:50 -07001190 id.resetSessionId();
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001191 mEnteredIds.add(id);
1192 }
1193
Philip P. Moltmann44611812017-02-23 12:52:46 -08001194 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001195 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001196 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001197 * @param view the virtual view parent.
1198 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -08001199 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001200 public void notifyViewExited(@NonNull View view, int virtualId) {
Felipe Leme54cc6832018-03-06 12:54:31 -08001201 if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
Svet Ganov43574b02017-04-12 09:25:20 -07001202 if (!hasAutofillFeature()) {
1203 return;
1204 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001205 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001206 notifyViewExitedLocked(view, virtualId);
1207 }
1208 }
Philip P. Moltmann44611812017-02-23 12:52:46 -08001209
Andreas Gampe3f24e692018-02-05 13:24:28 -08001210 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08001211 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
1212 ensureServiceClientAddedIfNeededLocked();
1213
Felipe Lemedf30f272019-03-19 13:47:48 -07001214 if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
Dake Gu67decfa2017-12-27 11:48:08 -08001215 // don't notify exited when Activity is already in background
1216 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme305fd402018-09-11 18:20:42 +00001217 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -08001218
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001219 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -07001220 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001221 }
Svet Ganov782043c2017-02-11 00:52:02 +00001222 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001223 }
1224
1225 /**
Felipe Leme640f30a2017-03-06 15:44:06 -08001226 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001227 *
Philip P. Moltmann44611812017-02-23 12:52:46 -08001228 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001229 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001230 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -07001231 if (!hasAutofillFeature()) {
1232 return;
1233 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001234 AutofillId id = null;
1235 boolean valueWasRead = false;
1236 AutofillValue value = null;
1237
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001238 synchronized (mLock) {
1239 // If the session is gone some fields might still be highlighted, hence we have to
1240 // remove the isAutofilled property even if no sessions are active.
1241 if (mLastAutofilledData == null) {
Adam He5061db82020-03-11 15:58:27 -07001242 view.setAutofilled(false, false);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001243 } else {
Felipe Leme42b97932018-02-20 13:04:31 -08001244 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001245 if (mLastAutofilledData.containsKey(id)) {
1246 value = view.getAutofillValue();
1247 valueWasRead = true;
Adam Hee2dbbce2020-03-25 13:30:24 -07001248 final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001249
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001250 if (Objects.equals(mLastAutofilledData.get(id), value)) {
Adam Hee2dbbce2020-03-25 13:30:24 -07001251 view.setAutofilled(true, hideHighlight);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001252 } else {
Adam He5061db82020-03-11 15:58:27 -07001253 view.setAutofilled(false, false);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001254 mLastAutofilledData.remove(id);
1255 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001256 } else {
Adam He5061db82020-03-11 15:58:27 -07001257 view.setAutofilled(false, false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001258 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001259 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001260
Felipe Lemedbb7a362019-05-13 15:28:46 -07001261 if (mForAugmentedAutofillOnly) {
1262 if (sVerbose) {
1263 Log.v(TAG, "notifyValueChanged(): not notifying system server on "
1264 + "augmented-only mode");
1265 }
1266 return;
1267 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001268 if (!mEnabled || !isActiveLocked()) {
lpetere28d6b02019-11-27 15:02:42 +08001269 if (!startAutofillIfNeededLocked(view)) {
1270 if (sVerbose) {
1271 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1272 + "): ignoring on state " + getStateAsStringLocked());
1273 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001274 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001275 return;
1276 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001277
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001278 if (id == null) {
Felipe Leme42b97932018-02-20 13:04:31 -08001279 id = view.getAutofillId();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001280 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001281
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001282 if (!valueWasRead) {
1283 value = view.getAutofillValue();
1284 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001285
Felipe Leme0aa4c502017-04-26 12:36:01 -07001286 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001287 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001288 }
1289
Felipe Lemebab851c2017-02-03 18:45:08 -08001290 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001291 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001292 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001293 * @param view the virtual view parent.
1294 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001295 * @param value new value of the child.
1296 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001297 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001298 if (!hasAutofillFeature()) {
1299 return;
1300 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001301 synchronized (mLock) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001302 if (mForAugmentedAutofillOnly) {
1303 if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
1304 return;
1305 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001306 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001307 if (sVerbose) {
1308 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1309 + "): ignoring on state " + getStateAsStringLocked());
1310 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001311 return;
1312 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001313
Felipe Leme305fd402018-09-11 18:20:42 +00001314 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001315 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001316 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001317 }
1318
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001319 /**
Felipe Leme67e62092018-02-15 14:47:31 -08001320 * Called to indicate a {@link View} is clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001321 *
Felipe Leme67e62092018-02-15 14:47:31 -08001322 * @param view view that has been clicked.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001323 */
Felipe Leme67e62092018-02-15 14:47:31 -08001324 public void notifyViewClicked(@NonNull View view) {
1325 notifyViewClicked(view.getAutofillId());
1326 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001327
Felipe Leme67e62092018-02-15 14:47:31 -08001328 /**
1329 * Called to indicate a virtual view has been clicked.
1330 *
1331 * @param view the virtual view parent.
1332 * @param virtualId id identifying the virtual child inside the parent view.
1333 */
1334 public void notifyViewClicked(@NonNull View view, int virtualId) {
Felipe Leme305fd402018-09-11 18:20:42 +00001335 notifyViewClicked(getAutofillId(view, virtualId));
Felipe Leme67e62092018-02-15 14:47:31 -08001336 }
1337
1338 private void notifyViewClicked(AutofillId id) {
1339 if (!hasAutofillFeature()) {
1340 return;
1341 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001342 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1343
1344 synchronized (mLock) {
Felipe Leme67e62092018-02-15 14:47:31 -08001345 if (!mEnabled || !isActiveLocked()) {
1346 return;
1347 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001348 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1349 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1350 commitLocked();
Felipe Leme11166522018-05-07 10:18:47 -07001351 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001352 }
1353 }
1354 }
1355
1356 /**
1357 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1358 *
1359 * @hide
1360 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001361 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001362 if (!hasAutofillFeature()) {
1363 return;
1364 }
1365 synchronized (mLock) {
1366 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001367 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001368 commitLocked();
1369 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001370 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001371 cancelLocked();
1372 }
1373 }
1374 }
1375
Felipe Lemebab851c2017-02-03 18:45:08 -08001376 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001377 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001378 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001379 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1380 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1381 * that represent the HTML elements, it should call this method after the form is submitted and
1382 * another page is rendered.
1383 *
1384 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1385 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001386 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001387 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001388 if (!hasAutofillFeature()) {
1389 return;
1390 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001391 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001392 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001393 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001394 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001395 }
1396
Andreas Gampe3f24e692018-02-05 13:24:28 -08001397 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001398 private void commitLocked() {
1399 if (!mEnabled && !isActiveLocked()) {
1400 return;
1401 }
1402 finishSessionLocked();
1403 }
1404
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001405 /**
1406 * Called to indicate the current autofill context should be cancelled.
1407 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001408 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1409 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1410 * that represent the HTML elements, it should call this method if the user does not post the
1411 * form but moves to another form in this page.
1412 *
1413 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1414 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001415 */
1416 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001417 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001418 if (!hasAutofillFeature()) {
1419 return;
1420 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001421 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001422 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001423 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001424 }
1425
Andreas Gampe3f24e692018-02-05 13:24:28 -08001426 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001427 private void cancelLocked() {
1428 if (!mEnabled && !isActiveLocked()) {
1429 return;
1430 }
1431 cancelSessionLocked();
1432 }
1433
Svet Ganovf965b872017-04-28 16:34:02 -07001434 /** @hide */
1435 public void disableOwnedAutofillServices() {
1436 disableAutofillServices();
1437 }
1438
Svet Ganovf20a0372017-04-10 17:08:05 -07001439 /**
1440 * If the app calling this API has enabled autofill services they
1441 * will be disabled.
1442 */
Svet Ganovf965b872017-04-28 16:34:02 -07001443 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001444 if (!hasAutofillFeature()) {
1445 return;
1446 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001447 try {
1448 mService.disableOwnedAutofillServices(mContext.getUserId());
1449 } catch (RemoteException e) {
1450 throw e.rethrowFromSystemServer();
1451 }
1452 }
1453
Felipe Lemedb041182017-04-21 17:33:38 -07001454 /**
1455 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1456 * enabled for the current user, or {@code false} otherwise.
1457 */
1458 public boolean hasEnabledAutofillServices() {
1459 if (mService == null) return false;
1460
Felipe Lemec0c15a32019-01-08 10:53:33 -08001461 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001462 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001463 mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
1464 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001465 } catch (RemoteException e) {
1466 throw e.rethrowFromSystemServer();
1467 }
1468 }
1469
1470 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001471 * Returns the component name of the {@link AutofillService} that is enabled for the current
1472 * user.
1473 */
1474 @Nullable
1475 public ComponentName getAutofillServiceComponentName() {
1476 if (mService == null) return null;
1477
Felipe Lemec0c15a32019-01-08 10:53:33 -08001478 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme23c75ff2017-12-14 13:27:44 -08001479 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001480 mService.getAutofillServiceComponentName(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001481 return receiver.getParcelableResult();
Felipe Leme23c75ff2017-12-14 13:27:44 -08001482 } catch (RemoteException e) {
1483 throw e.rethrowFromSystemServer();
1484 }
1485 }
1486
1487 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001488 * Gets the id of the {@link UserData} used for
1489 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1490 *
1491 * <p>This method is useful when the service must check the status of the {@link UserData} in
1492 * the device without fetching the whole object.
1493 *
1494 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1495 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1496 * the user.
1497 *
1498 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1499 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1500 * service for the user.
1501 */
1502 @Nullable public String getUserDataId() {
1503 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001504 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001505 mService.getUserDataId(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001506 return receiver.getStringResult();
Felipe Lemef0baef742018-01-26 14:39:39 -08001507 } catch (RemoteException e) {
1508 e.rethrowFromSystemServer();
1509 return null;
1510 }
1511 }
1512
1513 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001514 * Gets the user data used for
1515 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001516 *
Felipe Leme27f45732017-12-22 09:05:22 -08001517 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1518 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1519 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001520 *
Felipe Leme452886a2017-11-27 13:09:13 -08001521 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1522 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001523 */
Felipe Leme452886a2017-11-27 13:09:13 -08001524 @Nullable public UserData getUserData() {
1525 try {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001526 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemed4e52282018-06-18 13:56:38 -07001527 mService.getUserData(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001528 return receiver.getParcelableResult();
Felipe Leme452886a2017-11-27 13:09:13 -08001529 } catch (RemoteException e) {
1530 e.rethrowFromSystemServer();
1531 return null;
1532 }
1533 }
1534
1535 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001536 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001537 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001538 *
1539 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1540 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1541 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001542 */
Felipe Leme452886a2017-11-27 13:09:13 -08001543 public void setUserData(@Nullable UserData userData) {
1544 try {
1545 mService.setUserData(userData);
1546 } catch (RemoteException e) {
1547 e.rethrowFromSystemServer();
1548 }
1549 }
1550
1551 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001552 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1553 * enabled.
1554 *
1555 * <p>As field classification is an expensive operation, it could be disabled, either
1556 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1557 * permanently (for example, because the device is a low-level device).
1558 *
1559 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1560 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1561 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001562 */
Felipe Leme329d0402017-12-06 09:22:43 -08001563 public boolean isFieldClassificationEnabled() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001564 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme329d0402017-12-06 09:22:43 -08001565 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001566 mService.isFieldClassificationEnabled(receiver);
1567 return receiver.getIntResult() == 1;
Felipe Leme329d0402017-12-06 09:22:43 -08001568 } catch (RemoteException e) {
1569 e.rethrowFromSystemServer();
1570 return false;
1571 }
1572 }
1573
1574 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001575 * Gets the name of the default algorithm used for
1576 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1577 *
1578 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1579 * set.
1580 *
1581 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1582 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1583 * the user.
1584 */
1585 @Nullable
1586 public String getDefaultFieldClassificationAlgorithm() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001587 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001588 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001589 mService.getDefaultFieldClassificationAlgorithm(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001590 return receiver.getStringResult();
Felipe Leme27f45732017-12-22 09:05:22 -08001591 } catch (RemoteException e) {
1592 e.rethrowFromSystemServer();
1593 return null;
1594 }
1595 }
1596
1597 /**
1598 * Gets the name of all algorithms currently available for
1599 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1600 *
1601 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001602 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1603 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001604 */
1605 @NonNull
1606 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemec0c15a32019-01-08 10:53:33 -08001607 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Leme27f45732017-12-22 09:05:22 -08001608 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001609 mService.getAvailableFieldClassificationAlgorithms(receiver);
Felipe Lemec0c15a32019-01-08 10:53:33 -08001610 final String[] algorithms = receiver.getStringArrayResult();
Felipe Lemee4ac7402018-01-16 19:37:00 -08001611 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001612 } catch (RemoteException e) {
1613 e.rethrowFromSystemServer();
1614 return null;
1615 }
1616 }
1617
1618 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001619 * Returns {@code true} if autofill is supported by the current device and
1620 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001621 *
1622 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1623 * <ol>
1624 * <li>Low-end devices.
1625 * <li>Device policy rules that forbid its usage.
1626 * </ol>
1627 */
1628 public boolean isAutofillSupported() {
1629 if (mService == null) return false;
1630
Felipe Lemec0c15a32019-01-08 10:53:33 -08001631 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedb041182017-04-21 17:33:38 -07001632 try {
Felipe Lemed4e52282018-06-18 13:56:38 -07001633 mService.isServiceSupported(mContext.getUserId(), receiver);
1634 return receiver.getIntResult() == 1;
Felipe Lemedb041182017-04-21 17:33:38 -07001635 } catch (RemoteException e) {
1636 throw e.rethrowFromSystemServer();
1637 }
1638 }
1639
Felipe Leme637e05e2017-12-06 12:09:37 -08001640 // Note: don't need to use locked suffix because mContext is final.
1641 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001642 final AutofillClient client = mContext.getAutofillClient();
Felipe Leme261e75f2018-12-17 10:15:08 -08001643 if (client == null && sVerbose) {
1644 Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
Felipe Leme686128e2017-10-17 14:02:20 -07001645 + mContext);
1646 }
1647 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001648 }
1649
Dake Gud9dbd272018-03-13 11:38:42 -07001650 /**
1651 * Check if autofill ui is showing, must be called on UI thread.
1652 * @hide
1653 */
1654 public boolean isAutofillUiShowing() {
1655 final AutofillClient client = mContext.getAutofillClient();
Felipe Lemecb2e83d2018-03-19 11:15:00 -07001656 return client != null && client.autofillClientIsFillUiShowing();
Dake Gud9dbd272018-03-13 11:38:42 -07001657 }
1658
Svet Ganov782043c2017-02-11 00:52:02 +00001659 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001660 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001661 if (!hasAutofillFeature()) {
1662 return;
1663 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001664 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001665 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1666 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1667 // service set the extra and returned RESULT_CANCELED...
1668
Felipe Leme1ad95f02018-06-15 13:07:34 -07001669 if (sDebug) {
1670 Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
1671 }
Felipe Lemed633f072017-02-14 10:17:17 -08001672
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001673 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001674 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001675 return;
1676 }
Dake Gu67decfa2017-12-27 11:48:08 -08001677 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1678 // of app activity. We enforce enter event to re-show fill ui in such case.
1679 // CTS example:
1680 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1681 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1682 if (!mOnInvisibleCalled && focusView != null
1683 && focusView.canNotifyAutofillEnterExitEvent()) {
1684 notifyViewExitedLocked(focusView);
1685 notifyViewEnteredLocked(focusView, 0);
1686 }
1687 if (data == null) {
1688 // data is set to null when result is not RESULT_OK
1689 return;
1690 }
1691
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001692 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1693 final Bundle responseData = new Bundle();
1694 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001695 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1696 if (newClientState != null) {
1697 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1698 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001699 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001700 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1701 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001702 } catch (RemoteException e) {
1703 Log.e(TAG, "Error delivering authentication result", e);
1704 }
Svet Ganov782043c2017-02-11 00:52:02 +00001705 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001706 }
1707
Felipe Leme42b97932018-02-20 13:04:31 -08001708 /**
1709 * Gets the next unique autofill ID for the activity context.
1710 *
1711 * <p>Typically used to manage views whose content is recycled - see
1712 * {@link View#setAutofillId(AutofillId)} for more info.
1713 *
1714 * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
1715 * the {@link Context} associated with this {@link AutofillManager}.
1716 */
1717 @Nullable
1718 public AutofillId getNextAutofillId() {
1719 final AutofillClient client = getClient();
1720 if (client == null) return null;
1721
1722 final AutofillId id = client.autofillClientGetNextAutofillId();
1723
1724 if (id == null && sDebug) {
1725 Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
1726 }
1727
1728 return id;
Felipe Leme0200d9e2017-01-24 15:10:26 -08001729 }
1730
Felipe Leme305fd402018-09-11 18:20:42 +00001731 private static AutofillId getAutofillId(View parent, int virtualId) {
1732 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001733 }
1734
Andreas Gampe3f24e692018-02-05 13:24:28 -08001735 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001736 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1737 @NonNull AutofillValue value, int flags) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001738 if (mEnteredForAugmentedAutofillIds != null
Felipe Lemedf30f272019-03-19 13:47:48 -07001739 && mEnteredForAugmentedAutofillIds.contains(id)
1740 || mEnabledForAugmentedAutofillOnly) {
Felipe Lemea7de4022019-02-19 17:16:45 -08001741 if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
Felipe Lemedf30f272019-03-19 13:47:48 -07001742 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
Felipe Lemea7de4022019-02-19 17:16:45 -08001743 }
Felipe Leme9f9ee252017-04-27 13:56:22 -07001744 if (sVerbose) {
1745 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001746 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001747 + ", compatMode=" + isCompatibilityModeEnabledLocked()
Felipe Lemedf30f272019-03-19 13:47:48 -07001748 + ", augmentedOnly=" + mForAugmentedAutofillOnly
1749 + ", enabledAugmentedOnly=" + mEnabledForAugmentedAutofillOnly
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001750 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001751 }
Felipe Lemee5db59d2019-05-07 11:10:07 -07001752 // We need to reset the augmented-only state when a manual request is made, as it's possible
1753 // that the service returned null for the first request and now the user is manually
1754 // requesting autofill to trigger a custom UI provided by the service.
1755 if (mForAugmentedAutofillOnly && !mEnabledForAugmentedAutofillOnly
1756 && (flags & FLAG_MANUAL_REQUEST) != 0) {
1757 if (sVerbose) {
1758 Log.v(TAG, "resetting mForAugmentedAutofillOnly on manual autofill request");
1759 }
1760 mForAugmentedAutofillOnly = false;
1761 }
Felipe Leme3103c632017-12-18 15:05:14 -08001762 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001763 if (sVerbose) {
1764 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001765 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001766 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001767 return;
1768 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001769 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001770 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001771 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001772
Felipe Lemec0c15a32019-01-08 10:53:33 -08001773 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemeadb34f52019-03-15 16:24:26 -07001774 final ComponentName componentName = client.autofillClientGetComponentName();
Joanne Chung9e247b12019-07-19 23:41:32 +08001775
1776 if (!mEnabledForAugmentedAutofillOnly && mOptions != null
1777 && mOptions.isAutofillDisabledLocked(componentName)) {
1778 if (mOptions.isAugmentedAutofillEnabled(mContext)) {
1779 if (sDebug) {
1780 Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
1781 + "whitelisted for augmented autofill");
1782 flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
1783 }
1784 } else {
1785 if (sDebug) {
1786 Log.d(TAG, "startSession(" + componentName + "): ignored because "
1787 + "disabled by service and not whitelisted for augmented autofill");
1788 }
1789 setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
1790 client.autofillClientResetableStateAvailable();
1791 return;
1792 }
1793 }
1794
Felipe Lemed4e52282018-06-18 13:56:38 -07001795 mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001796 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Lemeadb34f52019-03-15 16:24:26 -07001797 mCallback != null, flags, componentName,
Felipe Lemed4e52282018-06-18 13:56:38 -07001798 isCompatibilityModeEnabledLocked(), receiver);
1799 mSessionId = receiver.getIntResult();
Felipe Lemec24a56a2017-08-03 14:27:57 -07001800 if (mSessionId != NO_SESSION) {
1801 mState = STATE_ACTIVE;
1802 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07001803 final int extraFlags = receiver.getOptionalExtraIntResult(0);
Felipe Lemee5db59d2019-05-07 11:10:07 -07001804 if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
Felipe Lemeadb34f52019-03-15 16:24:26 -07001805 if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
1806 mForAugmentedAutofillOnly = true;
1807 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001808 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001809 } catch (RemoteException e) {
1810 throw e.rethrowFromSystemServer();
Joanne Chung1676bec2020-02-12 11:36:30 +08001811 } catch (SyncResultReceiver.TimeoutException e) {
1812 // no-op, just log the error message.
1813 Log.w(TAG, "Exception getting result from SyncResultReceiver: " + e);
Svet Ganov782043c2017-02-11 00:52:02 +00001814 }
1815 }
1816
Andreas Gampe3f24e692018-02-05 13:24:28 -08001817 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001818 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001819 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001820
1821 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001822
Svet Ganov782043c2017-02-11 00:52:02 +00001823 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001824 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001825 } catch (RemoteException e) {
1826 throw e.rethrowFromSystemServer();
1827 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001828
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001829 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001830 }
1831
Andreas Gampe3f24e692018-02-05 13:24:28 -08001832 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001833 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001834 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001835
1836 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001837
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001838 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001839 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001840 } catch (RemoteException e) {
1841 throw e.rethrowFromSystemServer();
1842 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001843
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001844 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001845 }
1846
Andreas Gampe3f24e692018-02-05 13:24:28 -08001847 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001848 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001849 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001850 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001851 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001852 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001853 mSaveTriggerId = null;
Dake Gub0fa3782018-02-26 12:25:14 -08001854 mIdShownFillUi = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001855 if (resetEnteredIds) {
1856 mEnteredIds = null;
1857 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001858 }
1859
Andreas Gampe3f24e692018-02-05 13:24:28 -08001860 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001861 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1862 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001863 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001864 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1865 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001866 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001867 try {
Felipe Lemed62b5162018-06-06 15:08:25 -07001868 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1869 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -08001870 } catch (RemoteException e) {
1871 throw e.rethrowFromSystemServer();
1872 }
1873 }
Svet Ganov782043c2017-02-11 00:52:02 +00001874
Andreas Gampe3f24e692018-02-05 13:24:28 -08001875 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001876 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Lemedf30f272019-03-19 13:47:48 -07001877 final AutofillClient client = getClient();
1878 if (client == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001879 return;
1880 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001881
Svet Ganov782043c2017-02-11 00:52:02 +00001882 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001883 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001884 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001885 final int userId = mContext.getUserId();
Felipe Lemec0c15a32019-01-08 10:53:33 -08001886 final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
Felipe Lemedf30f272019-03-19 13:47:48 -07001887 mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
1888 userId, receiver);
Felipe Lemed4e52282018-06-18 13:56:38 -07001889 final int flags = receiver.getIntResult();
Felipe Leme9f9ee252017-04-27 13:56:22 -07001890 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1891 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1892 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Felipe Lemedf30f272019-03-19 13:47:48 -07001893 mEnabledForAugmentedAutofillOnly = (flags
1894 & FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
1895 if (sVerbose) {
1896 Log.v(TAG, "receiver results: flags=" + flags + " enabled=" + mEnabled
1897 + ", enabledForAugmentedOnly: " + mEnabledForAugmentedAutofillOnly);
1898 }
Koji Fukuiccec6a62017-10-18 17:48:40 +09001899 final IAutoFillManager service = mService;
1900 final IAutoFillManagerClient serviceClient = mServiceClient;
1901 mServiceClientCleaner = Cleaner.create(this, () -> {
Felipe Leme559e21d2019-01-18 17:57:21 -08001902 // TODO(b/123100811): call service to also remove reference to
Felipe Leme284ad1c2018-11-15 18:16:12 -08001903 // mAugmentedAutofillServiceClient
Koji Fukuiccec6a62017-10-18 17:48:40 +09001904 try {
1905 service.removeClient(serviceClient, userId);
1906 } catch (RemoteException e) {
1907 }
1908 });
Svet Ganov782043c2017-02-11 00:52:02 +00001909 } catch (RemoteException e) {
1910 throw e.rethrowFromSystemServer();
1911 }
1912 }
1913 }
1914
lpetere28d6b02019-11-27 15:02:42 +08001915 @GuardedBy("mLock")
1916 private boolean startAutofillIfNeededLocked(View view) {
1917 if (mState == STATE_UNKNOWN
1918 && mSessionId == NO_SESSION
1919 && view instanceof EditText
1920 && !TextUtils.isEmpty(((EditText) view).getText())
1921 && !view.isFocused()
1922 && view.isImportantForAutofill()
1923 && view.isLaidOut()
1924 && view.isVisibleToUser()) {
1925
1926 ensureServiceClientAddedIfNeededLocked();
1927
1928 if (sVerbose) {
1929 Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
1930 }
1931 if (mEnabled && !isClientDisablingEnterExitEvent()) {
1932 final AutofillId id = view.getAutofillId();
1933 final AutofillValue value = view.getAutofillValue();
1934 // Starts new session.
1935 startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
1936 // Updates value.
1937 updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
1938 /* flags= */ 0);
1939 addEnteredIdLocked(id);
1940 return true;
1941 }
1942 }
1943 return false;
1944 }
1945
Felipe Lemee6010f22017-03-03 11:19:51 -08001946 /**
1947 * Registers a {@link AutofillCallback} to receive autofill events.
1948 *
1949 * @param callback callback to receive events.
1950 */
1951 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001952 if (!hasAutofillFeature()) {
1953 return;
1954 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001955 synchronized (mLock) {
1956 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001957
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001958 final boolean hadCallback = mCallback != null;
1959 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001960
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001961 if (!hadCallback) {
1962 try {
1963 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1964 } catch (RemoteException e) {
1965 throw e.rethrowFromSystemServer();
1966 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001967 }
1968 }
1969 }
1970
1971 /**
1972 * Unregisters a {@link AutofillCallback} to receive autofill events.
1973 *
1974 * @param callback callback to stop receiving events.
1975 */
1976 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001977 if (!hasAutofillFeature()) {
1978 return;
1979 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001980 synchronized (mLock) {
1981 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001982
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001983 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001984
Felipe Lemee6010f22017-03-03 11:19:51 -08001985 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001986 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001987 } catch (RemoteException e) {
1988 throw e.rethrowFromSystemServer();
1989 }
1990 }
1991 }
1992
Felipe Leme559e21d2019-01-18 17:57:21 -08001993 /**
Felipe Leme559e21d2019-01-18 17:57:21 -08001994 * Explicitly limits augmented autofill to the given packages and activities.
1995 *
Felipe Leme559e21d2019-01-18 17:57:21 -08001996 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
1997 *
1998 * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
1999 * apps that uses addresses. For example, if the service wants to support augmented autofill on
2000 * all activities of app {@code AddressApp1} and just activities {@code act1} and {@code act2}
2001 * of {@code AddressApp2}, it would call:
2002 * {@code setAugmentedAutofillWhitelist(Arrays.asList("AddressApp1"),
2003 * Arrays.asList(new ComponentName("AddressApp2", "act1"),
2004 * new ComponentName("AddressApp2", "act2")));}
2005 *
2006 * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
2007 * service, and it's ignored if the caller isn't it.
2008 *
2009 * @hide
2010 */
2011 @SystemApi
2012 @TestApi
Felipe Leme7a3c9f52019-02-13 16:32:49 -08002013 public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
2014 @Nullable Set<ComponentName> activities) {
Felipe Leme80e7bf12019-02-14 10:56:52 -08002015 if (!hasAutofillFeature()) {
2016 return;
2017 }
2018
2019 final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
2020 final int resultCode;
2021 try {
2022 mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
2023 resultReceiver);
2024 resultCode = resultReceiver.getIntResult();
2025 } catch (RemoteException e) {
2026 throw e.rethrowFromSystemServer();
2027 }
2028 switch (resultCode) {
2029 case RESULT_OK:
2030 return;
2031 case RESULT_CODE_NOT_SERVICE:
2032 throw new SecurityException("caller is not user's Augmented Autofill Service");
2033 default:
2034 Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
2035 + resultCode);
2036 }
2037 }
2038
Felipe Lemea7de4022019-02-19 17:16:45 -08002039 /**
2040 * Notifies that a non-autofillable view was entered because the activity is whitelisted for
2041 * augmented autofill.
2042 *
2043 * <p>This method is necessary to set the right flag on start, so the server-side session
2044 * doesn't trigger the standard autofill workflow, but the augmented's instead.
2045 *
2046 * @hide
2047 */
2048 public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
2049 final AutofillId id = view.getAutofillId();
2050 synchronized (mLock) {
2051 if (mEnteredForAugmentedAutofillIds == null) {
2052 mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
2053 }
2054 mEnteredForAugmentedAutofillIds.add(id);
2055 }
2056 }
2057
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002058 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2059 Rect anchorBounds, IAutofillWindowPresenter presenter) {
2060 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07002061 if (anchor == null) {
2062 return;
2063 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002064
2065 AutofillCallback callback = null;
2066 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002067 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002068 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002069
2070 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002071 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Dake Gub0fa3782018-02-26 12:25:14 -08002072 anchorBounds, presenter)) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002073 callback = mCallback;
Dake Gub0fa3782018-02-26 12:25:14 -08002074 mIdShownFillUi = id;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002075 }
2076 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002077 }
2078 }
2079
2080 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002081 if (id.isVirtualInt()) {
2082 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002083 AutofillCallback.EVENT_INPUT_SHOWN);
2084 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002085 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002086 }
2087 }
2088 }
2089
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002090 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08002091 Intent fillInIntent, boolean authenticateInline) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002092 synchronized (mLock) {
2093 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002094 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002095 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08002096 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
2097 // before onAuthenticationResult()
2098 mOnInvisibleCalled = false;
Adam He5be0f152020-02-03 15:53:32 -08002099 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
2100 authenticateInline);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002101 }
2102 }
2103 }
2104 }
2105
Dake Gu6a20a192018-02-08 12:09:30 -08002106 private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
2107 final View anchor = findView(id);
2108 if (anchor == null) {
2109 return;
2110 }
2111
Dake Gu6a20a192018-02-08 12:09:30 -08002112 synchronized (mLock) {
2113 if (mSessionId == sessionId) {
2114 AutofillClient client = getClient();
2115
2116 if (client != null) {
2117 client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
2118 }
2119 }
2120 }
2121 }
2122
Felipe Leme51e29da2017-10-24 14:03:10 -07002123 /** @hide */
2124 public static final int SET_STATE_FLAG_ENABLED = 0x01;
2125 /** @hide */
2126 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
2127 /** @hide */
2128 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
2129 /** @hide */
2130 public static final int SET_STATE_FLAG_DEBUG = 0x08;
2131 /** @hide */
2132 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
Felipe Lemee5db59d2019-05-07 11:10:07 -07002133 /** @hide */
2134 public static final int SET_STATE_FLAG_FOR_AUTOFILL_ONLY = 0x20;
Felipe Leme51e29da2017-10-24 14:03:10 -07002135
2136 private void setState(int flags) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002137 if (sVerbose) {
2138 Log.v(TAG, "setState(" + flags + ": " + DebugUtils.flagsToString(AutofillManager.class,
2139 "SET_STATE_FLAG_", flags) + ")");
2140 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002141 synchronized (mLock) {
Felipe Lemee5db59d2019-05-07 11:10:07 -07002142 if ((flags & SET_STATE_FLAG_FOR_AUTOFILL_ONLY) != 0) {
2143 mForAugmentedAutofillOnly = true;
2144 // NOTE: returning right away as this is the only flag set, at least currently...
2145 return;
2146 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002147 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
2148 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002149 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002150 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07002151 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002152 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002153 // Reset connection to system
2154 mServiceClient = null;
Felipe Leme284ad1c2018-11-15 18:16:12 -08002155 mAugmentedAutofillServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09002156 if (mServiceClientCleaner != null) {
2157 mServiceClientCleaner.clean();
2158 mServiceClientCleaner = null;
2159 }
Joanne Chung9e247b12019-07-19 23:41:32 +08002160 notifyReenableAutofill();
Svet Ganov48f10a22017-04-26 18:49:30 -07002161 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002162 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002163 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
2164 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002165 }
2166
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002167 /**
2168 * Sets a view as autofilled if the current value is the {code targetValue}.
2169 *
2170 * @param view The view that is to be autofilled
2171 * @param targetValue The value we want to fill into view
2172 */
Adam He5061db82020-03-11 15:58:27 -07002173 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
2174 boolean hideHighlight) {
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002175 AutofillValue currentValue = view.getAutofillValue();
2176 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002177 synchronized (mLock) {
2178 if (mLastAutofilledData == null) {
2179 mLastAutofilledData = new ParcelableMap(1);
2180 }
Felipe Leme42b97932018-02-20 13:04:31 -08002181 mLastAutofilledData.put(view.getAutofillId(), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002182 }
Adam He5061db82020-03-11 15:58:27 -07002183 view.setAutofilled(true, hideHighlight);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002184 }
2185 }
2186
Adam He5061db82020-03-11 15:58:27 -07002187 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
2188 boolean hideHighlight) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002189 synchronized (mLock) {
2190 if (sessionId != mSessionId) {
2191 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07002192 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002193
Felipe Leme637e05e2017-12-06 12:09:37 -08002194 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002195 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002196 return;
2197 }
2198
2199 final int itemCount = ids.size();
2200 int numApplied = 0;
2201 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002202 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
2203 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002204
Felipe Leme49f08ed2018-03-26 16:18:45 -07002205 ArrayList<AutofillId> failedIds = null;
2206
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002207 for (int i = 0; i < itemCount; i++) {
2208 final AutofillId id = ids.get(i);
2209 final AutofillValue value = values.get(i);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002210 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002211 if (view == null) {
Felipe Leme49f08ed2018-03-26 16:18:45 -07002212 // Most likely view has been removed after the initial request was sent to the
2213 // the service; this is fine, but we need to update the view status in the
2214 // server side so it can be triggered again.
2215 Log.d(TAG, "autofill(): no View with id " + id);
2216 if (failedIds == null) {
2217 failedIds = new ArrayList<>();
2218 }
2219 failedIds.add(id);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002220 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07002221 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002222 if (id.isVirtualInt()) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002223 if (virtualValues == null) {
2224 // Most likely there will be just one view with virtual children.
2225 virtualValues = new ArrayMap<>(1);
2226 }
2227 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
2228 if (valuesByParent == null) {
2229 // We don't know the size yet, but usually it will be just a few fields...
2230 valuesByParent = new SparseArray<>(5);
2231 virtualValues.put(view, valuesByParent);
2232 }
Felipe Lemea75333c2019-01-22 15:45:32 -08002233 valuesByParent.put(id.getVirtualChildIntId(), value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002234 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002235 // Mark the view as to be autofilled with 'value'
2236 if (mLastAutofilledData == null) {
2237 mLastAutofilledData = new ParcelableMap(itemCount - i);
2238 }
2239 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002240
2241 view.autofill(value);
2242
2243 // Set as autofilled if the values match now, e.g. when the value was updated
2244 // synchronously.
2245 // If autofill happens async, the view is set to autofilled in
2246 // notifyValueChanged.
Adam He5061db82020-03-11 15:58:27 -07002247 setAutofilledIfValuesIs(view, value, hideHighlight);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002248
2249 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07002250 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002251 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002252
Felipe Leme49f08ed2018-03-26 16:18:45 -07002253 if (failedIds != null) {
2254 if (sVerbose) {
2255 Log.v(TAG, "autofill(): total failed views: " + failedIds);
2256 }
2257 try {
2258 mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
2259 } catch (RemoteException e) {
2260 // In theory, we could ignore this error since it's not a big deal, but
2261 // in reality, we rather crash the app anyways, as the failure could be
2262 // a consequence of something going wrong on the server side...
2263 e.rethrowFromSystemServer();
2264 }
2265 }
2266
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002267 if (virtualValues != null) {
2268 for (int i = 0; i < virtualValues.size(); i++) {
2269 final View parent = virtualValues.keyAt(i);
2270 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
2271 parent.autofill(childrenValues);
2272 numApplied += childrenValues.size();
Felipe Leme49f08ed2018-03-26 16:18:45 -07002273 // TODO: we should provide a callback so the parent can call failures; something
2274 // like notifyAutofillFailed(View view, int[] childrenIds);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002275 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002276 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002277
Felipe Leme11166522018-05-07 10:18:47 -07002278 mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
Felipe Lemeb22d6352017-09-08 20:03:53 -07002279 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
Felipe Leme11166522018-05-07 10:18:47 -07002280 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002281 }
Felipe Leme4753bb02017-03-22 20:24:00 -07002282 }
2283
Felipe Leme11166522018-05-07 10:18:47 -07002284 private LogMaker newLog(int category) {
Felipe Lemeb838a092018-05-22 14:56:15 -07002285 final LogMaker log = new LogMaker(category)
Felipe Leme833c99b2018-05-24 10:41:48 -07002286 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
2287
2288 if (isCompatibilityModeEnabledLocked()) {
2289 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
2290 }
Felipe Lemeb838a092018-05-22 14:56:15 -07002291 final AutofillClient client = getClient();
2292 if (client == null) {
2293 // Client should never be null here, but it doesn't hurt to check...
2294 log.setPackageName(mContext.getPackageName());
2295 } else {
2296 log.setComponentName(client.autofillClientGetComponentName());
2297 }
2298 return log;
Felipe Leme11166522018-05-07 10:18:47 -07002299 }
2300
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002301 /**
2302 * Set the tracked views.
2303 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002304 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002305 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002306 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07002307 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002308 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002309 */
Felipe Leme27e20222017-05-18 15:24:11 -07002310 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002311 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
2312 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Felipe Leme785777b2019-04-30 18:12:50 -07002313 if (saveTriggerId != null) {
2314 saveTriggerId.resetSessionId();
2315 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002316 synchronized (mLock) {
Felipe Leme159cb002019-03-27 10:44:59 -07002317 if (sVerbose) {
2318 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
2319 + ", trackedIds=" + Arrays.toString(trackedIds)
2320 + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible
2321 + ", saveOnFinish=" + saveOnFinish
2322 + ", fillableIds=" + Arrays.toString(fillableIds)
2323 + ", saveTrigerId=" + saveTriggerId
2324 + ", mFillableIds=" + mFillableIds
2325 + ", mEnabled=" + mEnabled
2326 + ", mSessionId=" + mSessionId);
2327
2328 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002329 if (mEnabled && mSessionId == sessionId) {
2330 if (saveOnAllViewsInvisible) {
2331 mTrackedViews = new TrackedViews(trackedIds);
2332 } else {
2333 mTrackedViews = null;
2334 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002335 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07002336 if (fillableIds != null) {
2337 if (mFillableIds == null) {
2338 mFillableIds = new ArraySet<>(fillableIds.length);
2339 }
2340 for (AutofillId id : fillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002341 id.resetSessionId();
Felipe Leme27e20222017-05-18 15:24:11 -07002342 mFillableIds.add(id);
2343 }
Felipe Leme27e20222017-05-18 15:24:11 -07002344 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002345
2346 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
2347 // Turn off trigger on previous view id.
2348 setNotifyOnClickLocked(mSaveTriggerId, false);
2349 }
2350
2351 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
2352 // Turn on trigger on new view id.
2353 mSaveTriggerId = saveTriggerId;
2354 setNotifyOnClickLocked(mSaveTriggerId, true);
2355 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002356 }
2357 }
2358 }
2359
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002360 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
2361 final View view = findView(id);
2362 if (view == null) {
2363 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
2364 return;
2365 }
2366 view.setNotifyAutofillManagerOnClick(notify);
2367 }
2368
Felipe Lemec24a56a2017-08-03 14:27:57 -07002369 private void setSaveUiState(int sessionId, boolean shown) {
2370 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
2371 synchronized (mLock) {
2372 if (mSessionId != NO_SESSION) {
2373 // Race condition: app triggered a new session after the previous session was
2374 // finished but before server called setSaveUiState() - need to cancel the new
2375 // session to avoid further inconsistent behavior.
2376 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
2377 + ") called on existing session " + mSessionId + "; cancelling it");
2378 cancelSessionLocked();
2379 }
2380 if (shown) {
2381 mSessionId = sessionId;
2382 mState = STATE_SHOWING_SAVE_UI;
2383 } else {
2384 mSessionId = NO_SESSION;
2385 mState = STATE_UNKNOWN;
2386 }
2387 }
2388 }
2389
Felipe Leme650f7ab2017-09-19 13:08:24 -07002390 /**
2391 * Marks the state of the session as finished.
2392 *
2393 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme0c8ce322018-03-23 13:54:22 -07002394 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
2395 * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
Felipe Lemed9dc9542018-09-19 11:54:28 -07002396 * changed on compat mode), {@link #STATE_UNKNOWN_FAILED} (because the session was finished
2397 * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE}
2398 * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill
2399 * service disabled further autofill requests for the activity).
Felipe Leme6e43dd32019-03-13 17:25:33 -07002400 * @param autofillableIds list of ids that could trigger autofill, use to not handle a new
2401 * session when they're entered.
Felipe Leme650f7ab2017-09-19 13:08:24 -07002402 */
Felipe Leme6e43dd32019-03-13 17:25:33 -07002403 private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
Felipe Leme785777b2019-04-30 18:12:50 -07002404 if (autofillableIds != null) {
2405 for (int i = 0; i < autofillableIds.size(); i++) {
2406 autofillableIds.get(i).resetSessionId();
2407 }
2408 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002409 synchronized (mLock) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002410 if (sVerbose) {
2411 Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
Felipe Leme6e43dd32019-03-13 17:25:33 -07002412 + getStateAsString(newState) + "; autofillableIds=" + autofillableIds);
2413 }
2414 if (autofillableIds != null) {
2415 mEnteredIds = new ArraySet<>(autofillableIds);
Felipe Leme0c8ce322018-03-23 13:54:22 -07002416 }
Felipe Lemed9dc9542018-09-19 11:54:28 -07002417 if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002418 resetSessionLocked(/* resetEnteredIds= */ true);
2419 mState = STATE_UNKNOWN;
2420 } else {
2421 resetSessionLocked(/* resetEnteredIds= */ false);
2422 mState = newState;
2423 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002424 }
2425 }
2426
Felipe Leme284ad1c2018-11-15 18:16:12 -08002427 /**
2428 * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
2429 *
2430 * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
2431 * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
2432 */
2433 private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
2434 synchronized (mLock) {
2435 if (mAugmentedAutofillServiceClient == null) {
2436 mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
2437 }
2438 final Bundle resultData = new Bundle();
2439 resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
2440 mAugmentedAutofillServiceClient.asBinder());
2441
2442 try {
2443 result.send(0, resultData);
2444 } catch (RemoteException e) {
2445 Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
2446 }
2447 }
2448 }
2449
Feng Cao43c20432020-03-26 17:57:34 -07002450 private void requestShowSoftInput(@NonNull AutofillId id) {
2451 if (sVerbose) Log.v(TAG, "requestShowSoftInput(" + id + ")");
2452 final AutofillClient client = getClient();
2453 if (client == null) {
2454 return;
2455 }
2456 final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
2457 if (view == null) {
2458 if (sVerbose) Log.v(TAG, "View is not found");
2459 return;
2460 }
2461 final Handler handler = view.getHandler();
2462 if (handler == null) {
2463 if (sVerbose) Log.v(TAG, "Ignoring requestShowSoftInput due to no handler in view");
2464 return;
2465 }
2466 if (handler.getLooper() != Looper.myLooper()) {
2467 // The view is running on a different thread than our own, so we need to reschedule
2468 // our work for over there.
2469 if (sVerbose) Log.v(TAG, "Scheduling showSoftInput() on the view UI thread");
2470 handler.post(() -> requestShowSoftInputInViewThread(view));
2471 } else {
2472 requestShowSoftInputInViewThread(view);
2473 }
2474 }
2475
2476 // This method must be called from within the View thread.
2477 private static void requestShowSoftInputInViewThread(@NonNull View view) {
2478 if (!view.isFocused()) {
2479 Log.w(TAG, "Ignoring requestShowSoftInput() due to non-focused view");
2480 return;
2481 }
2482 final InputMethodManager inputMethodManager = view.getContext().getSystemService(
2483 InputMethodManager.class);
2484 boolean ret = inputMethodManager.showSoftInput(view, /*flags=*/ 0);
2485 if (sVerbose) Log.v(TAG, " InputMethodManager.showSoftInput returns " + ret);
2486 }
2487
Dake Gub0fa3782018-02-26 12:25:14 -08002488 /** @hide */
2489 public void requestHideFillUi() {
2490 requestHideFillUi(mIdShownFillUi, true);
2491 }
2492
2493 private void requestHideFillUi(AutofillId id, boolean force) {
2494 final View anchor = id == null ? null : findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07002495 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002496 if (anchor == null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002497 if (force) {
2498 // When user taps outside autofill window, force to close fill ui even id does
2499 // not match.
2500 AutofillClient client = getClient();
2501 if (client != null) {
2502 client.autofillClientRequestHideFillUi();
2503 }
2504 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002505 return;
2506 }
Felipe Leme27e20222017-05-18 15:24:11 -07002507 requestHideFillUi(id, anchor);
2508 }
2509
2510 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002511
2512 AutofillCallback callback = null;
2513 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07002514 // We do not check the session id for two reasons:
2515 // 1. If local and remote session id are off sync the UI would be stuck shown
2516 // 2. There is a race between the user state being destroyed due the fill
2517 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08002518 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07002519 if (client != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08002520 if (client.autofillClientRequestHideFillUi()) {
2521 mIdShownFillUi = null;
Svet Ganov48f10a22017-04-26 18:49:30 -07002522 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002523 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002524 }
2525 }
2526
2527 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002528 if (id.isVirtualInt()) {
2529 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002530 AutofillCallback.EVENT_INPUT_HIDDEN);
2531 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002532 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07002533 }
2534 }
2535 }
2536
Joanne Chung9e247b12019-07-19 23:41:32 +08002537 private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
2538 synchronized (mLock) {
2539 if (mOptions == null) {
2540 return;
2541 }
2542 long expiration = SystemClock.elapsedRealtime() + disableDuration;
2543 // Protect it against overflow
2544 if (expiration < 0) {
2545 expiration = Long.MAX_VALUE;
2546 }
2547 if (componentName != null) {
2548 if (mOptions.disabledActivities == null) {
2549 mOptions.disabledActivities = new ArrayMap<>();
2550 }
2551 mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
2552 } else {
2553 mOptions.appDisabledExpiration = expiration;
2554 }
2555 }
2556 }
2557
2558 void notifyReenableAutofill() {
2559 synchronized (mLock) {
2560 if (mOptions == null) {
2561 return;
2562 }
2563 mOptions.appDisabledExpiration = 0;
2564 mOptions.disabledActivities = null;
2565 }
2566 }
2567
Felipe Leme17292d12017-10-24 14:03:10 -07002568 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002569 if (sVerbose) {
2570 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07002571 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07002572 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002573 final View anchor = findView(id);
2574 if (anchor == null) {
2575 return;
2576 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002577
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002578 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002579 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002580 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07002581 callback = mCallback;
2582 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002583 }
2584
2585 if (callback != null) {
Felipe Lemea75333c2019-01-22 15:45:32 -08002586 if (id.isVirtualInt()) {
2587 callback.onAutofillEvent(anchor, id.getVirtualChildIntId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07002588 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
2589 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002590 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07002591 }
Felipe Lemec7b45292017-09-19 09:06:20 -07002592 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07002593
Felipe Lemee5db59d2019-05-07 11:10:07 -07002594 if (sessionFinishedState != STATE_UNKNOWN) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002595 // Callback call was "hijacked" to also update the session state.
Felipe Leme6e43dd32019-03-13 17:25:33 -07002596 setSessionFinished(sessionFinishedState, /* autofillableIds= */ null);
Felipe Leme4753bb02017-03-22 20:24:00 -07002597 }
2598 }
2599
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002600 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002601 * Find a single view by its id.
2602 *
2603 * @param autofillId The autofill id of the view
2604 *
2605 * @return The view or {@code null} if view was not found
2606 */
2607 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002608 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002609 if (client != null) {
2610 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08002611 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002612 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08002613 }
2614
Felipe Lemebb810922017-04-25 15:54:06 -07002615 /** @hide */
2616 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07002617 return mService != null;
2618 }
2619
Felipe Lemec24a56a2017-08-03 14:27:57 -07002620 /** @hide */
2621 public void onPendingSaveUi(int operation, IBinder token) {
2622 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
2623
2624 synchronized (mLock) {
2625 try {
2626 mService.onPendingSaveUi(operation, token);
2627 } catch (RemoteException e) {
2628 e.rethrowFromSystemServer();
2629 }
2630 }
2631 }
2632
2633 /** @hide */
2634 public void dump(String outerPrefix, PrintWriter pw) {
2635 pw.print(outerPrefix); pw.println("AutofillManager:");
2636 final String pfx = outerPrefix + " ";
2637 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07002638 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07002639 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Lemeb546ca72018-08-15 08:44:12 -07002640 final AutofillClient client = getClient();
2641 if (client != null) {
2642 pw.print(pfx); pw.print("client: "); pw.print(client);
2643 pw.print(" ("); pw.print(client.autofillClientGetActivityToken()); pw.println(')');
2644 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002645 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
Felipe Lemedf30f272019-03-19 13:47:48 -07002646 pw.print(pfx); pw.print("enabledAugmentedOnly: "); pw.println(mForAugmentedAutofillOnly);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002647 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
2648 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08002649 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002650 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
Felipe Lemef5e26302018-06-08 16:26:39 -07002651 pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002652 pw.print(pfx); pw.print("tracked views: ");
2653 if (mTrackedViews == null) {
2654 pw.println("null");
2655 } else {
2656 final String pfx2 = pfx + " ";
2657 pw.println();
2658 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
2659 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
2660 }
2661 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08002662 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Lemea7de4022019-02-19 17:16:45 -08002663 if (mEnteredForAugmentedAutofillIds != null) {
2664 pw.print(pfx); pw.print("entered ids for augmented autofill: ");
2665 pw.println(mEnteredForAugmentedAutofillIds);
2666 }
Felipe Lemeadb34f52019-03-15 16:24:26 -07002667 if (mForAugmentedAutofillOnly) {
2668 pw.print(pfx); pw.println("For Augmented Autofill Only");
2669 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002670 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
2671 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Lemea4f39cd2019-02-19 15:08:59 -08002672 if (mOptions != null) {
2673 pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
2674 }
Felipe Lemeb546ca72018-08-15 08:44:12 -07002675 pw.print(pfx); pw.print("compat mode enabled: ");
2676 synchronized (mLock) {
2677 if (mCompatibilityBridge != null) {
2678 final String pfx2 = pfx + " ";
2679 pw.println("true");
2680 pw.print(pfx2); pw.print("windowId: ");
2681 pw.println(mCompatibilityBridge.mFocusedWindowId);
2682 pw.print(pfx2); pw.print("nodeId: ");
2683 pw.println(mCompatibilityBridge.mFocusedNodeId);
2684 pw.print(pfx2); pw.print("virtualId: ");
2685 pw.println(AccessibilityNodeInfo
2686 .getVirtualDescendantId(mCompatibilityBridge.mFocusedNodeId));
2687 pw.print(pfx2); pw.print("focusedBounds: ");
2688 pw.println(mCompatibilityBridge.mFocusedBounds);
2689 } else {
2690 pw.println("false");
2691 }
2692 }
Felipe Leme51e29da2017-10-24 14:03:10 -07002693 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
2694 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002695 }
2696
Andreas Gampe3f24e692018-02-05 13:24:28 -08002697 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002698 private String getStateAsStringLocked() {
Felipe Leme0c8ce322018-03-23 13:54:22 -07002699 return getStateAsString(mState);
2700 }
2701
2702 @NonNull
2703 private static String getStateAsString(int state) {
2704 switch (state) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002705 case STATE_UNKNOWN:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002706 return "UNKNOWN";
Felipe Lemec7b45292017-09-19 09:06:20 -07002707 case STATE_ACTIVE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002708 return "ACTIVE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002709 case STATE_FINISHED:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002710 return "FINISHED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002711 case STATE_SHOWING_SAVE_UI:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002712 return "SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002713 case STATE_DISABLED_BY_SERVICE:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002714 return "DISABLED_BY_SERVICE";
2715 case STATE_UNKNOWN_COMPAT_MODE:
2716 return "UNKNOWN_COMPAT_MODE";
Felipe Lemed9dc9542018-09-19 11:54:28 -07002717 case STATE_UNKNOWN_FAILED:
2718 return "UNKNOWN_FAILED";
Felipe Lemec7b45292017-09-19 09:06:20 -07002719 default:
Felipe Leme0c8ce322018-03-23 13:54:22 -07002720 return "INVALID:" + state;
Felipe Lemec7b45292017-09-19 09:06:20 -07002721 }
2722 }
2723
Felipe Leme559e21d2019-01-18 17:57:21 -08002724 /** @hide */
2725 public static String getSmartSuggestionModeToString(@SmartSuggestionMode int flags) {
Felipe Lemecc510272019-02-07 14:56:27 -08002726 switch (flags) {
2727 case FLAG_SMART_SUGGESTION_OFF:
2728 return "OFF";
2729 case FLAG_SMART_SUGGESTION_SYSTEM:
2730 return "SYSTEM";
2731 default:
2732 return "INVALID:" + flags;
2733 }
Felipe Leme559e21d2019-01-18 17:57:21 -08002734 }
2735
Andreas Gampe3f24e692018-02-05 13:24:28 -08002736 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002737 private boolean isActiveLocked() {
2738 return mState == STATE_ACTIVE;
2739 }
2740
Andreas Gampe3f24e692018-02-05 13:24:28 -08002741 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002742 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002743 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002744 }
2745
Andreas Gampe3f24e692018-02-05 13:24:28 -08002746 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002747 private boolean isFinishedLocked() {
2748 return mState == STATE_FINISHED;
2749 }
2750
Felipe Leme9876a6f2017-05-30 15:47:28 -07002751 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002752 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002753 if (client == null) {
2754 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2755 return;
2756 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002757 client.autofillClientRunOnUiThread(runnable);
2758 }
2759
2760 /**
2761 * Implementation of the accessibility based compatibility.
2762 */
2763 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2764 @GuardedBy("mLock")
2765 private final Rect mFocusedBounds = new Rect();
2766 @GuardedBy("mLock")
2767 private final Rect mTempBounds = new Rect();
2768
2769 @GuardedBy("mLock")
2770 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2771 @GuardedBy("mLock")
2772 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2773
2774 // Need to report a fake service in case a11y clients check the service list
2775 @NonNull
2776 @GuardedBy("mLock")
2777 AccessibilityServiceInfo mCompatServiceInfo;
2778
2779 CompatibilityBridge() {
2780 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2781 am.setAccessibilityPolicy(this);
2782 }
2783
2784 private AccessibilityServiceInfo getCompatServiceInfo() {
2785 synchronized (mLock) {
2786 if (mCompatServiceInfo != null) {
2787 return mCompatServiceInfo;
2788 }
2789 final Intent intent = new Intent();
2790 intent.setComponent(new ComponentName("android",
2791 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2792 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2793 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2794 try {
2795 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2796 } catch (XmlPullParserException | IOException e) {
2797 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2798 throw new IllegalStateException("Cannot find compat autofill service");
2799 }
2800 return mCompatServiceInfo;
2801 }
2802 }
2803
2804 @Override
2805 public boolean isEnabled(boolean accessibilityEnabled) {
2806 return true;
2807 }
2808
2809 @Override
2810 public int getRelevantEventTypes(int relevantEventTypes) {
2811 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2812 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
Felipe Leme54cc6832018-03-06 12:54:31 -08002813 | AccessibilityEvent.TYPE_VIEW_CLICKED
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002814 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2815 }
2816
2817 @Override
2818 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2819 List<AccessibilityServiceInfo> installedServices) {
2820 if (installedServices == null) {
2821 installedServices = new ArrayList<>();
2822 }
2823 installedServices.add(getCompatServiceInfo());
2824 return installedServices;
2825 }
2826
2827 @Override
2828 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2829 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2830 if (enabledService == null) {
2831 enabledService = new ArrayList<>();
2832 }
2833 enabledService.add(getCompatServiceInfo());
2834 return enabledService;
2835 }
2836
2837 @Override
2838 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2839 boolean accessibilityEnabled, int relevantEventTypes) {
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002840 final int type = event.getEventType();
2841 if (sVerbose) {
2842 // NOTE: this is waaay spammy, but that's life.
2843 Log.v(TAG, "onAccessibilityEvent(" + AccessibilityEvent.eventTypeToString(type)
Felipe Leme7d4461d2018-08-16 10:49:03 -07002844 + "): virtualId="
2845 + AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId())
2846 + ", client=" + getClient());
Felipe Leme5d35d3d2018-08-13 18:15:32 -07002847 }
2848 switch (type) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002849 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2850 synchronized (mLock) {
2851 if (mFocusedWindowId == event.getWindowId()
2852 && mFocusedNodeId == event.getSourceNodeId()) {
2853 return event;
2854 }
2855 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2856 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2857 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2858 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2859 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2860 mFocusedBounds.set(0, 0, 0, 0);
2861 }
2862 final int windowId = event.getWindowId();
2863 final long nodeId = event.getSourceNodeId();
2864 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2865 mFocusedWindowId = windowId;
2866 mFocusedNodeId = nodeId;
2867 }
2868 }
2869 } break;
2870
2871 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2872 synchronized (mLock) {
2873 if (mFocusedWindowId == event.getWindowId()
2874 && mFocusedNodeId == event.getSourceNodeId()) {
2875 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2876 }
2877 }
2878 } break;
2879
Felipe Leme54cc6832018-03-06 12:54:31 -08002880 case AccessibilityEvent.TYPE_VIEW_CLICKED: {
2881 synchronized (mLock) {
2882 notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
2883 }
2884 } break;
2885
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002886 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2887 final AutofillClient client = getClient();
2888 if (client != null) {
2889 synchronized (mLock) {
2890 if (client.autofillClientIsFillUiShowing()) {
2891 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2892 }
2893 updateTrackedViewsLocked();
2894 }
2895 }
2896 } break;
2897 }
2898
2899 return accessibilityEnabled ? event : null;
2900 }
2901
2902 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2903 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2904 if (!isVirtualNode(virtualId)) {
2905 return false;
2906 }
2907 final View view = findViewByAccessibilityId(windowId, nodeId);
2908 if (view == null) {
2909 return false;
2910 }
2911 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2912 if (node == null) {
2913 return false;
2914 }
2915 if (!node.isEditable()) {
2916 return false;
2917 }
2918 final Rect newBounds = mTempBounds;
2919 node.getBoundsInScreen(newBounds);
2920 if (newBounds.equals(focusedBounds)) {
2921 return false;
2922 }
2923 focusedBounds.set(newBounds);
2924 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2925 return true;
2926 }
2927
2928 private void notifyViewExited(int windowId, long nodeId) {
2929 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2930 if (!isVirtualNode(virtualId)) {
2931 return;
2932 }
2933 final View view = findViewByAccessibilityId(windowId, nodeId);
2934 if (view == null) {
2935 return;
2936 }
2937 AutofillManager.this.notifyViewExited(view, virtualId);
2938 }
2939
2940 private void notifyValueChanged(int windowId, long nodeId) {
2941 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2942 if (!isVirtualNode(virtualId)) {
2943 return;
2944 }
2945 final View view = findViewByAccessibilityId(windowId, nodeId);
2946 if (view == null) {
2947 return;
2948 }
2949 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2950 if (node == null) {
2951 return;
2952 }
2953 AutofillManager.this.notifyValueChanged(view, virtualId,
2954 AutofillValue.forText(node.getText()));
2955 }
2956
Felipe Leme54cc6832018-03-06 12:54:31 -08002957 private void notifyViewClicked(int windowId, long nodeId) {
2958 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2959 if (!isVirtualNode(virtualId)) {
2960 return;
2961 }
2962 final View view = findViewByAccessibilityId(windowId, nodeId);
2963 if (view == null) {
2964 return;
2965 }
2966 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2967 if (node == null) {
2968 return;
2969 }
2970 AutofillManager.this.notifyViewClicked(view, virtualId);
2971 }
2972
Andreas Gampe3f24e692018-02-05 13:24:28 -08002973 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002974 private void updateTrackedViewsLocked() {
2975 if (mTrackedViews != null) {
2976 mTrackedViews.onVisibleForAutofillChangedLocked();
2977 }
2978 }
2979
2980 private View findViewByAccessibilityId(int windowId, long nodeId) {
2981 final AutofillClient client = getClient();
2982 if (client == null) {
2983 return null;
2984 }
2985 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2986 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2987 }
2988
2989 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2990 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2991 if (provider == null) {
2992 return null;
2993 }
2994 return provider.createAccessibilityNodeInfo(virtualId);
2995 }
2996
2997 private boolean isVirtualNode(int nodeId) {
2998 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2999 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
3000 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07003001 }
3002
Felipe Lemee6010f22017-03-03 11:19:51 -08003003 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003004 * View tracking information. Once all tracked views become invisible the session is finished.
3005 */
3006 private class TrackedViews {
3007 /** Visible tracked views */
3008 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
3009
3010 /** Invisible tracked views */
3011 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
3012
3013 /**
3014 * Check if set is null or value is in set.
3015 *
3016 * @param set The set or null (== empty set)
3017 * @param value The value that might be in the set
3018 *
3019 * @return {@code true} iff set is not empty and value is in set
3020 */
Felipe Leme27e20222017-05-18 15:24:11 -07003021 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003022 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
3023 return set != null && set.contains(value);
3024 }
3025
3026 /**
3027 * Add a value to a set. If set is null, create a new set.
3028 *
3029 * @param set The set or null (== empty set)
3030 * @param valueToAdd The value to add
3031 *
3032 * @return The set including the new value. If set was {@code null}, a set containing only
3033 * the new value.
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 @NonNull
3037 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
3038 if (set == null) {
3039 set = new ArraySet<>(1);
3040 }
3041
3042 set.add(valueToAdd);
3043
3044 return set;
3045 }
3046
3047 /**
3048 * Remove a value from a set.
3049 *
3050 * @param set The set or null (== empty set)
3051 * @param valueToRemove The value to remove
3052 *
3053 * @return The set without the removed value. {@code null} if set was null, or is empty
3054 * after removal.
3055 */
Felipe Leme27e20222017-05-18 15:24:11 -07003056 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003057 @Nullable
3058 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
3059 if (set == null) {
3060 return null;
3061 }
3062
3063 set.remove(valueToRemove);
3064
3065 if (set.isEmpty()) {
3066 return null;
3067 }
3068
3069 return set;
3070 }
3071
3072 /**
3073 * Set the tracked views.
3074 *
3075 * @param trackedIds The views to be tracked
3076 */
Felipe Leme27e20222017-05-18 15:24:11 -07003077 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08003078 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003079 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003080 final boolean[] isVisible;
3081
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003082 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003083 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003084 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003085 } else {
3086 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07003087 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003088 }
3089
Felipe Leme27e20222017-05-18 15:24:11 -07003090 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003091 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07003092 final AutofillId id = trackedIds[i];
Felipe Leme785777b2019-04-30 18:12:50 -07003093 id.resetSessionId();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003094
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003095 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07003096 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003097 } else {
3098 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3099 }
3100 }
3101 }
3102
Felipe Leme9f9ee252017-04-27 13:56:22 -07003103 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003104 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003105 + " mVisibleTrackedIds=" + mVisibleTrackedIds
3106 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
3107 }
3108
3109 if (mVisibleTrackedIds == null) {
3110 finishSessionLocked();
3111 }
3112 }
3113
3114 /**
3115 * Called when a {@link View view's} visibility changes.
3116 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07003117 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003118 * @param isVisible visible if the view is visible in the view hierarchy.
3119 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003120 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08003121 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07003122 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003123 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003124 + isVisible);
3125 }
3126
Dake Gu67decfa2017-12-27 11:48:08 -08003127 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003128 if (isVisible) {
3129 if (isInSet(mInvisibleTrackedIds, id)) {
3130 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
3131 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
3132 }
3133 } else {
3134 if (isInSet(mVisibleTrackedIds, id)) {
3135 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
3136 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
3137 }
3138 }
3139 }
3140
3141 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07003142 if (sVerbose) {
3143 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
3144 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003145 finishSessionLocked();
3146 }
3147 }
3148
3149 /**
3150 * Called once the client becomes visible.
3151 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003152 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003153 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08003154 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003155 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003156 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003157 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08003158 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003159 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
3160 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
3161 if (client != null) {
Felipe Leme7008e702018-03-16 18:02:16 -07003162 if (sVerbose) {
3163 Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
3164 + " vis=" + mVisibleTrackedIds);
3165 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003166 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003167 final ArrayList<AutofillId> orderedInvisibleIds =
3168 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003169 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3170 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003171
3172 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
3173 for (int i = 0; i < numInvisibleTrackedIds; i++) {
3174 final AutofillId id = orderedInvisibleIds.get(i);
3175 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003176 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3177
Felipe Leme9f9ee252017-04-27 13:56:22 -07003178 if (sDebug) {
3179 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003180 }
3181 } else {
3182 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3183 }
3184 }
3185 }
3186
3187 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003188 final ArrayList<AutofillId> orderedVisibleIds =
3189 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08003190 final boolean[] isVisible = client.autofillClientGetViewVisibility(
3191 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003192
3193 final int numVisibleTrackedIds = orderedVisibleIds.size();
3194 for (int i = 0; i < numVisibleTrackedIds; i++) {
3195 final AutofillId id = orderedVisibleIds.get(i);
3196
3197 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003198 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
3199 } else {
3200 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
3201
Felipe Leme9f9ee252017-04-27 13:56:22 -07003202 if (sDebug) {
3203 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003204 }
3205 }
3206 }
3207 }
3208
3209 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
3210 mVisibleTrackedIds = updatedVisibleTrackedIds;
3211 }
3212
3213 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08003214 if (sVerbose) {
3215 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
3216 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003217 finishSessionLocked();
3218 }
3219 }
3220 }
3221
3222 /**
Felipe Leme744976e2017-07-31 11:34:14 -07003223 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08003224 *
3225 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003226 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003227 */
3228 public abstract static class AutofillCallback {
3229
3230 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07003231 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
3232 EVENT_INPUT_SHOWN,
3233 EVENT_INPUT_HIDDEN,
3234 EVENT_INPUT_UNAVAILABLE
3235 })
Felipe Lemee6010f22017-03-03 11:19:51 -08003236 @Retention(RetentionPolicy.SOURCE)
3237 public @interface AutofillEventType {}
3238
3239 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003240 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08003241 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003242 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08003243 * should be hidden upon receiving this event.
3244 */
3245 public static final int EVENT_INPUT_SHOWN = 1;
3246
3247 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003248 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08003249 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003250 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08003251 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
3252 */
3253 public static final int EVENT_INPUT_HIDDEN = 2;
3254
3255 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003256 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07003257 * autofill is not available.
3258 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003259 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07003260 * to avoid flickering, it could shown it upon receiving this event.
3261 */
3262 public static final int EVENT_INPUT_UNAVAILABLE = 3;
3263
3264 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08003265 * Called after a change in the autofill state associated with a view.
3266 *
3267 * @param view view associated with the change.
3268 *
3269 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3270 */
Felipe Leme81f01d92017-03-16 17:13:25 -07003271 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
3272 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003273
3274 /**
3275 * Called after a change in the autofill state associated with a virtual view.
3276 *
3277 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07003278 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08003279 *
3280 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
3281 */
Felipe Leme6dcec872017-05-25 11:24:23 -07003282 public void onAutofillEvent(@NonNull View view, int virtualId,
3283 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07003284 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003285 }
3286
Felipe Leme640f30a2017-03-06 15:44:06 -08003287 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
3288 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00003289
Felipe Leme284ad1c2018-11-15 18:16:12 -08003290 private AutofillManagerClient(AutofillManager autofillManager) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003291 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00003292 }
3293
3294 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07003295 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003296 final AutofillManager afm = mAfm.get();
3297 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07003298 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00003299 }
3300 }
3301
3302 @Override
Adam He5061db82020-03-11 15:58:27 -07003303 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3304 boolean hideHighlight) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003305 final AutofillManager afm = mAfm.get();
3306 if (afm != null) {
Adam He5061db82020-03-11 15:58:27 -07003307 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
Svet Ganov782043c2017-02-11 00:52:02 +00003308 }
3309 }
3310
3311 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07003312 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
Adam He5be0f152020-02-03 15:53:32 -08003313 Intent fillInIntent, boolean authenticateInline) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003314 final AutofillManager afm = mAfm.get();
3315 if (afm != null) {
Adam He5be0f152020-02-03 15:53:32 -08003316 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
3317 authenticateInline));
Svet Ganov782043c2017-02-11 00:52:02 +00003318 }
3319 }
Felipe Lemee6010f22017-03-03 11:19:51 -08003320
3321 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003322 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3323 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08003324 final AutofillManager afm = mAfm.get();
3325 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003326 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3327 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07003328 }
3329 }
3330
3331 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07003332 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003333 final AutofillManager afm = mAfm.get();
3334 if (afm != null) {
Dake Gub0fa3782018-02-26 12:25:14 -08003335 afm.post(() -> afm.requestHideFillUi(id, false));
Felipe Leme4753bb02017-03-22 20:24:00 -07003336 }
3337 }
3338
3339 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07003340 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07003341 final AutofillManager afm = mAfm.get();
3342 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07003343 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08003344 }
3345 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07003346
3347 @Override
Joanne Chung9e247b12019-07-19 23:41:32 +08003348 public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
3349 throws RemoteException {
3350 final AutofillManager afm = mAfm.get();
3351 if (afm != null) {
3352 afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
3353 }
3354 }
3355
3356 @Override
Dake Gu6a20a192018-02-08 12:09:30 -08003357 public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
3358 final AutofillManager afm = mAfm.get();
3359 if (afm != null) {
3360 afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
3361 }
3362 }
3363
3364 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07003365 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003366 final AutofillManager afm = mAfm.get();
3367 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07003368 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07003369 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07003370 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07003371 } catch (IntentSender.SendIntentException e) {
3372 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
3373 }
3374 });
3375 }
3376 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003377
3378 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07003379 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003380 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
3381 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003382 final AutofillManager afm = mAfm.get();
3383 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07003384 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
3385 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07003386 }
3387 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07003388
3389 @Override
3390 public void setSaveUiState(int sessionId, boolean shown) {
3391 final AutofillManager afm = mAfm.get();
3392 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003393 afm.post(() -> afm.setSaveUiState(sessionId, shown));
3394 }
3395 }
3396
3397 @Override
Felipe Leme6e43dd32019-03-13 17:25:33 -07003398 public void setSessionFinished(int newState, List<AutofillId> autofillableIds) {
Felipe Lemec7b45292017-09-19 09:06:20 -07003399 final AutofillManager afm = mAfm.get();
3400 if (afm != null) {
Felipe Leme6e43dd32019-03-13 17:25:33 -07003401 afm.post(() -> afm.setSessionFinished(newState, autofillableIds));
Felipe Lemec24a56a2017-08-03 14:27:57 -07003402 }
3403 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003404
3405 @Override
3406 public void getAugmentedAutofillClient(IResultReceiver result) {
3407 final AutofillManager afm = mAfm.get();
3408 if (afm != null) {
3409 afm.post(() -> afm.getAugmentedAutofillClient(result));
3410 }
3411 }
Feng Cao43c20432020-03-26 17:57:34 -07003412
3413 @Override
3414 public void requestShowSoftInput(@NonNull AutofillId id) {
3415 final AutofillManager afm = mAfm.get();
3416 if (afm != null) {
3417 afm.post(() -> afm.requestShowSoftInput(id));
3418 }
3419 }
Felipe Leme284ad1c2018-11-15 18:16:12 -08003420 }
3421
3422 private static final class AugmentedAutofillManagerClient
3423 extends IAugmentedAutofillManagerClient.Stub {
3424 private final WeakReference<AutofillManager> mAfm;
3425
3426 private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
3427 mAfm = new WeakReference<>(autofillManager);
3428 }
3429
3430 @Override
3431 public Rect getViewCoordinates(@NonNull AutofillId id) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003432 final AutofillManager afm = mAfm.get();
3433 if (afm == null) return null;
3434
Feng Cao0fd56412020-02-13 19:58:30 -08003435 final View view = getView(afm, id);
Adam He234030a2019-03-15 15:57:57 -07003436 if (view == null) {
Adam He234030a2019-03-15 15:57:57 -07003437 return null;
3438 }
Feng Cao023b84c2018-12-14 15:51:17 -08003439 final Rect windowVisibleDisplayFrame = new Rect();
3440 view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
Felipe Leme284ad1c2018-11-15 18:16:12 -08003441 final int[] location = new int[2];
3442 view.getLocationOnScreen(location);
Feng Cao023b84c2018-12-14 15:51:17 -08003443 final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
3444 location[0] + view.getWidth(),
3445 location[1] - windowVisibleDisplayFrame.top + view.getHeight());
Felipe Leme284ad1c2018-11-15 18:16:12 -08003446 if (sVerbose) {
3447 Log.v(TAG, "Coordinates for " + id + ": " + rect);
3448 }
3449 return rect;
3450 }
3451
3452 @Override
Adam He5061db82020-03-11 15:58:27 -07003453 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
3454 boolean hideHighlight) {
Felipe Leme284ad1c2018-11-15 18:16:12 -08003455 final AutofillManager afm = mAfm.get();
3456 if (afm != null) {
Adam He5061db82020-03-11 15:58:27 -07003457 afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
Felipe Leme284ad1c2018-11-15 18:16:12 -08003458 }
3459 }
Feng Cao158b6562019-01-07 15:42:52 -08003460
3461 @Override
3462 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
3463 Rect anchorBounds, IAutofillWindowPresenter presenter) {
3464 final AutofillManager afm = mAfm.get();
3465 if (afm != null) {
3466 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
3467 presenter));
3468 }
3469 }
3470
3471 @Override
3472 public void requestHideFillUi(int sessionId, AutofillId id) {
3473 final AutofillManager afm = mAfm.get();
3474 if (afm != null) {
3475 afm.post(() -> afm.requestHideFillUi(id, false));
3476 }
3477 }
Feng Cao0fd56412020-02-13 19:58:30 -08003478
3479 @Override
3480 public boolean requestAutofill(int sessionId, AutofillId id) {
3481 final AutofillManager afm = mAfm.get();
3482 if (afm == null || afm.mSessionId != sessionId) {
3483 if (sDebug) {
3484 Slog.d(TAG, "Autofill not available or sessionId doesn't match");
3485 }
3486 return false;
3487 }
3488 final View view = getView(afm, id);
3489 if (view == null || !view.isFocused()) {
3490 if (sDebug) {
3491 Slog.d(TAG, "View not available or is not on focus");
3492 }
3493 return false;
3494 }
3495 if (sVerbose) {
3496 Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
3497 }
3498 afm.post(() -> afm.requestAutofill(view));
3499 return true;
3500 }
3501
3502 @Nullable
3503 private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
3504 final AutofillClient client = afm.getClient();
3505 if (client == null) {
3506 Log.w(TAG, "getView(" + id + "): no autofill client");
3507 return null;
3508 }
3509 View view = client.autofillClientFindViewByAutofillIdTraversal(id);
3510 if (view == null) {
3511 Log.w(TAG, "getView(" + id + "): could not find view");
3512 }
3513 return view;
3514 }
Svet Ganov782043c2017-02-11 00:52:02 +00003515 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08003516}