blob: 95c12fb8c1dbf05a55269df90217fe819980c842 [file] [log] [blame]
Felipe Leme3461d3c2017-01-19 08:54:55 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view.autofill;
18
felipealfd3f8f62018-02-07 11:49:07 +010019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
22
Svetoslav Ganov24c90452017-12-27 15:17:14 -080023import android.accessibilityservice.AccessibilityServiceInfo;
Felipe Lemee6010f22017-03-03 11:19:51 -080024import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080025import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080026import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060027import android.annotation.SystemService;
Felipe Leme17292d12017-10-24 14:03:10 -070028import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080029import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000030import android.content.Intent;
31import android.content.IntentSender;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080032import android.content.pm.PackageManager;
33import android.content.pm.ResolveInfo;
Felipe Leme3461d3c2017-01-19 08:54:55 -080034import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070035import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000036import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070037import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000038import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080039import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070040import android.service.autofill.AutofillService;
41import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080042import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070043import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070044import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080045import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070046import android.util.SparseArray;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080047import android.view.Choreographer;
Felipe Leme3461d3c2017-01-19 08:54:55 -080048import android.view.View;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080049import android.view.accessibility.AccessibilityEvent;
50import android.view.accessibility.AccessibilityManager;
51import android.view.accessibility.AccessibilityNodeInfo;
52import android.view.accessibility.AccessibilityNodeProvider;
53import android.view.accessibility.AccessibilityWindowInfo;
felipealfd3f8f62018-02-07 11:49:07 +010054
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070055import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070056import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070057import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080058import com.android.internal.util.ArrayUtils;
Felipe Leme637e05e2017-12-06 12:09:37 -080059import com.android.internal.util.Preconditions;
felipealfd3f8f62018-02-07 11:49:07 +010060
Svetoslav Ganov24c90452017-12-27 15:17:14 -080061import org.xmlpull.v1.XmlPullParserException;
Felipe Leme4753bb02017-03-22 20:24:00 -070062
Svetoslav Ganov24c90452017-12-27 15:17:14 -080063import java.io.IOException;
Felipe Lemec24a56a2017-08-03 14:27:57 -070064import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080065import java.lang.annotation.Retention;
66import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000067import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070068import java.util.ArrayList;
Felipe Lemebc055b02018-01-05 17:04:10 -080069import java.util.Arrays;
Felipe Leme27f45732017-12-22 09:05:22 -080070import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000071import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070072import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000073
felipealfd3f8f62018-02-07 11:49:07 +010074import sun.misc.Cleaner;
Svetoslav Ganov24c90452017-12-27 15:17:14 -080075
Koji Fukuiccec6a62017-10-18 17:48:40 +090076// TODO: use java.lang.ref.Cleaner once Android supports Java 9
Koji Fukuiccec6a62017-10-18 17:48:40 +090077
Felipe Leme3461d3c2017-01-19 08:54:55 -080078/**
Felipe Leme744976e2017-07-31 11:34:14 -070079 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
80 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070081 *
Felipe Leme744976e2017-07-31 11:34:14 -070082 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
83 * activity context; the autofill context is created when one of the following methods is called for
84 * the first time in an activity context, and the current user has an enabled autofill service:
85 *
86 * <ul>
87 * <li>{@link #notifyViewEntered(View)}
88 * <li>{@link #notifyViewEntered(View, int, Rect)}
89 * <li>{@link #requestAutofill(View)}
90 * </ul>
91 *
92 * <p>Tipically, the context is automatically created when the first view of the activity is
93 * focused because {@code View.onFocusChanged()} indirectly calls
94 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
95 * explicitly create it (for example, a custom view developer could offer a contextual menu action
96 * in a text-field view to let users manually request autofill).
97 *
98 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
99 * that represents the view hierarchy by calling
100 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
101 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
102 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
103 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
104 * the hierarchy.
105 *
106 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
107 * parses it looking for views that can be autofilled. If the service finds such views, it returns
108 * a data structure to the Android System containing the following optional info:
109 *
110 * <ul>
111 * <li>Datasets used to autofill subsets of views in the activity.
112 * <li>Id of views that the service can save their values for future autofilling.
113 * </ul>
114 *
115 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700116 * UI associated with the view, when the view is focused on and is part of a dataset.
117 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700118 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700119 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700120 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
121 *
122 * <p>When the service returns ids of savable views, the Android System keeps track of changes
123 * made to these views, so they can be used to determine if the autofill save UI is shown later.
124 *
125 * <p>The context is then finished when one of the following occurs:
126 *
127 * <ul>
128 * <li>{@link #commit()} is called or all savable views are gone.
129 * <li>{@link #cancel()} is called.
130 * </ul>
131 *
132 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700133 * 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 -0700134 * option to Save, the current value of the views is then sent to the autofill service.
135 *
136 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800137 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600138@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800139public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800140
Felipe Leme640f30a2017-03-06 15:44:06 -0800141 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800142
Svet Ganov782043c2017-02-11 00:52:02 +0000143 /**
144 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700145 *
Svet Ganov782043c2017-02-11 00:52:02 +0000146 * <p>
147 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000148 */
149 public static final String EXTRA_ASSIST_STRUCTURE =
150 "android.view.autofill.extra.ASSIST_STRUCTURE";
151
152 /**
153 * Intent extra: The result of an authentication operation. It is
154 * either a fully populated {@link android.service.autofill.FillResponse}
155 * or a fully populated {@link android.service.autofill.Dataset} if
156 * a response or a dataset is being authenticated respectively.
157 *
158 * <p>
159 * Type: {@link android.service.autofill.FillResponse} or a
160 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000161 */
162 public static final String EXTRA_AUTHENTICATION_RESULT =
163 "android.view.autofill.extra.AUTHENTICATION_RESULT";
164
Felipe Leme7320ca92017-03-29 15:09:54 -0700165 /**
166 * Intent extra: The optional extras provided by the
167 * {@link android.service.autofill.AutofillService}.
168 *
169 * <p>For example, when the service responds to a {@link
170 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
171 * a {@code FillResponse} that requires authentication, the Intent that launches the
172 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700173 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700174 *
Felipe Lemea9372382017-10-09 14:42:36 -0700175 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
176 * can also add this bundle to the {@link Intent} set as the
177 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
178 * so the bundle can be recovered later on
179 * {@link android.service.autofill.SaveRequest#getClientState()}.
180 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700181 * <p>
182 * Type: {@link android.os.Bundle}
183 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700184 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600185 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700186
Felipe Lemec24a56a2017-08-03 14:27:57 -0700187 /** @hide */
188 public static final String EXTRA_RESTORE_SESSION_TOKEN =
189 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
190
191 private static final String SESSION_ID_TAG = "android:sessionId";
192 private static final String STATE_TAG = "android:state";
193 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
194
Felipe Leme0aa4c502017-04-26 12:36:01 -0700195 /** @hide */ public static final int ACTION_START_SESSION = 1;
196 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
197 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
198 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800199
Felipe Leme9f9ee252017-04-27 13:56:22 -0700200
201 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
202 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
203 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
204
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700205 /** Which bits in an authentication id are used for the dataset id */
206 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
207 /** How many bits in an authentication id are used for the dataset id */
208 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
209 /** @hide The index for an undefined data set */
210 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
211
212 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700213 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
214 *
215 * @hide
216 */
217 public static final int PENDING_UI_OPERATION_CANCEL = 1;
218
219 /**
220 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
221 *
222 * @hide
223 */
224 public static final int PENDING_UI_OPERATION_RESTORE = 2;
225
226 /**
227 * Initial state of the autofill context, set when there is no session (i.e., when
228 * {@link #mSessionId} is {@link #NO_SESSION}).
229 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700230 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
231 * the server.
232 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700233 * @hide
234 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700235 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700236
237 /**
238 * State where the autofill context hasn't been {@link #commit() finished} nor
239 * {@link #cancel() canceled} yet.
240 *
241 * @hide
242 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700243 public static final int STATE_ACTIVE = 1;
244
245 /**
246 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700247 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700248 *
249 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
250 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
251 *
252 * @hide
253 */
254 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700255
256 /**
257 * State where the autofill context has been {@link #commit() finished} but the server still has
258 * a session because the Save UI hasn't been dismissed yet.
259 *
260 * @hide
261 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700262 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700263
264 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700265 * State where the autofill is disabled because the service cannot autofill the activity at all.
266 *
267 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
268 * (and {@link #requestAutofill(View, int, Rect)}).
269 *
270 * @hide
271 */
272 public static final int STATE_DISABLED_BY_SERVICE = 4;
273
274 /**
Felipe Lemebc055b02018-01-05 17:04:10 -0800275 * Timeout in ms for calls to the field classification service.
276 * @hide
277 */
278 public static final int FC_SERVICE_TIMEOUT = 5000;
279
280 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700281 * Makes an authentication id from a request id and a dataset id.
282 *
283 * @param requestId The request id.
284 * @param datasetId The dataset id.
285 * @return The authentication id.
286 * @hide
287 */
288 public static int makeAuthenticationId(int requestId, int datasetId) {
289 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
290 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
291 }
292
293 /**
294 * Gets the request id from an authentication id.
295 *
296 * @param authRequestId The authentication id.
297 * @return The request id.
298 * @hide
299 */
300 public static int getRequestIdFromAuthenticationId(int authRequestId) {
301 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
302 }
303
304 /**
305 * Gets the dataset id from an authentication id.
306 *
307 * @param authRequestId The authentication id.
308 * @return The dataset id.
309 * @hide
310 */
311 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
312 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
313 }
314
Felipe Leme4753bb02017-03-22 20:24:00 -0700315 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000316
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700317 /**
318 * There is currently no session running.
319 * {@hide}
320 */
321 public static final int NO_SESSION = Integer.MIN_VALUE;
322
Svet Ganov782043c2017-02-11 00:52:02 +0000323 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700324
325 private final Object mLock = new Object();
326
327 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000328 private IAutoFillManagerClient mServiceClient;
329
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700330 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900331 private Cleaner mServiceClientCleaner;
332
333 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800334 private AutofillCallback mCallback;
335
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700336 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000337
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700338 @GuardedBy("mLock")
339 private int mSessionId = NO_SESSION;
340
341 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700342 private int mState = STATE_UNKNOWN;
343
344 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000345 private boolean mEnabled;
346
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700347 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700348 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700349 @Nullable private ParcelableMap mLastAutofilledData;
350
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700351 /** If view tracking is enabled, contains the tracking state */
352 @GuardedBy("mLock")
353 @Nullable private TrackedViews mTrackedViews;
354
Felipe Leme27e20222017-05-18 15:24:11 -0700355 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
356 @GuardedBy("mLock")
357 @Nullable private ArraySet<AutofillId> mFillableIds;
358
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800359 /**
360 * Views that were already "entered" - if they're entered again when the session is not active,
361 * they're ignored
362 * */
363 @GuardedBy("mLock")
364 @Nullable private ArraySet<AutofillId> mEnteredIds;
365
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700366 /** If set, session is commited when the field is clicked. */
367 @GuardedBy("mLock")
368 @Nullable private AutofillId mSaveTriggerId;
369
Dake Gu67decfa2017-12-27 11:48:08 -0800370 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
371 @GuardedBy("mLock")
372 private boolean mOnInvisibleCalled;
373
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700374 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
375 @GuardedBy("mLock")
376 private boolean mSaveOnFinish;
377
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800378 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
379 @GuardedBy("mLock")
380 private CompatibilityBridge mCompatibilityBridge;
381
Svet Ganov782043c2017-02-11 00:52:02 +0000382 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800383 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000384 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000385 * Asks the client to start an authentication flow.
386 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700387 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000388 * @param intent The authentication intent.
389 * @param fillInIntent The authentication fill-in intent.
390 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800391 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700392 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800393
394 /**
395 * Tells the client this manager has state to be reset.
396 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800397 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700398
399 /**
400 * Request showing the autofill UI.
401 *
402 * @param anchor The real view the UI needs to anchor to.
403 * @param width The width of the fill UI content.
404 * @param height The height of the fill UI content.
405 * @param virtualBounds The bounds of the virtual decendant of the anchor.
406 * @param presenter The presenter that controls the fill UI window.
407 * @return Whether the UI was shown.
408 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800409 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700410 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
411
412 /**
413 * Request hiding the autofill UI.
414 *
415 * @return Whether the UI was hidden.
416 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800417 boolean autofillClientRequestHideFillUi();
418
419 /**
420 * Gets whether the fill UI is currenlty being shown.
421 *
422 * @return Whether the fill UI is currently being shown
423 */
424 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700425
426 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700427 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700428 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700429 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700430 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800431 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700432
433 /**
434 * Checks is the client is currently visible as understood by autofill.
435 *
436 * @return {@code true} if the client is currently visible
437 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800438 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700439
440 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800441 * Client might disable enter/exit event e.g. when activity is paused.
442 */
443 boolean isDisablingEnterExitEventForAutofill();
444
445 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700446 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700447 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800448 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700449 *
Felipe Leme27e20222017-05-18 15:24:11 -0700450 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700451 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800452 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
453 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700454
455 /**
456 * Finds a view by traversing the hierarchies of the client.
457 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800458 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700459 *
460 * @return The view, or {@code null} if not found
461 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800462 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
463
464 /**
465 * Finds a view by a11y id in a given client window.
466 *
467 * @param viewId The accessibility id of the views to find
468 * @param windowId The accessibility window id where to search
469 *
470 * @return The view, or {@code null} if not found
471 */
472 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700473
474 /**
475 * Runs the specified action on the UI thread.
476 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800477 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700478
479 /**
480 * Gets the complete component name of this client.
481 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800482 ComponentName autofillClientGetComponentName();
483
484 /**
485 * Gets the activity token
486 */
487 @Nullable IBinder autofillClientGetActivityToken();
488
489 /**
490 * @return Whether compatibility mode is enabled.
491 */
492 boolean autofillIsCompatibilityModeEnabled();
Svet Ganov782043c2017-02-11 00:52:02 +0000493 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800494
495 /**
496 * @hide
497 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800498 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800499 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800500 mService = service;
501 }
502
503 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800504 * @hide
505 */
506 public void enableCompatibilityMode() {
507 synchronized (mLock) {
508 // The accessibility manager is a singleton so we may need to plug
509 // different bridge based on which activity is currently focused
510 // in the current process. Since compat would be rarely used, just
511 // create and register a new instance every time.
512 mCompatibilityBridge = new CompatibilityBridge();
513 }
514 }
515
516 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700517 * Restore state after activity lifecycle
518 *
519 * @param savedInstanceState The state to be restored
520 *
521 * {@hide}
522 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700523 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700524 if (!hasAutofillFeature()) {
525 return;
526 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700527 synchronized (mLock) {
528 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
529
Felipe Lemec24a56a2017-08-03 14:27:57 -0700530 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700531 Log.w(TAG, "New session was started before onCreate()");
532 return;
533 }
534
535 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700536 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700537
538 if (mSessionId != NO_SESSION) {
539 ensureServiceClientAddedIfNeededLocked();
540
Felipe Leme637e05e2017-12-06 12:09:37 -0800541 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700542 if (client != null) {
543 try {
544 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800545 client.autofillClientGetActivityToken(),
546 mServiceClient.asBinder());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700547
548 if (!sessionWasRestored) {
549 Log.w(TAG, "Session " + mSessionId + " could not be restored");
550 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700551 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700552 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700553 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700554 Log.d(TAG, "session " + mSessionId + " was restored");
555 }
556
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800557 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700558 }
559 } catch (RemoteException e) {
560 Log.e(TAG, "Could not figure out if there was an autofill session", e);
561 }
562 }
563 }
564 }
565 }
566
567 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700568 * Called once the client becomes visible.
569 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800570 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700571 *
572 * {@hide}
573 */
574 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800575 // This gets called when the client just got visible at which point the visibility
576 // of the tracked views may not have been computed (due to a pending layout, etc).
577 // While generally we have no way to know when the UI has settled. We will evaluate
578 // the tracked views state at the end of next frame to guarantee that everything
579 // that may need to be laid out is laid out.
580 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
581 synchronized (mLock) {
582 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
583 mTrackedViews.onVisibleForAutofillChangedLocked();
584 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700585 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800586 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700587 }
588
589 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800590 * Called once the client becomes invisible.
591 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800592 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800593 *
594 * {@hide}
595 */
596 public void onInvisibleForAutofill() {
597 synchronized (mLock) {
598 mOnInvisibleCalled = true;
599 }
600 }
601
602 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700603 * Save state before activity lifecycle
604 *
605 * @param outState Place to store the state
606 *
607 * {@hide}
608 */
609 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700610 if (!hasAutofillFeature()) {
611 return;
612 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700613 synchronized (mLock) {
614 if (mSessionId != NO_SESSION) {
615 outState.putInt(SESSION_ID_TAG, mSessionId);
616 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700617 if (mState != STATE_UNKNOWN) {
618 outState.putInt(STATE_TAG, mState);
619 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700620 if (mLastAutofilledData != null) {
621 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
622 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700623 }
624 }
625
626 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800627 * @hide
628 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800629 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100630 public boolean isCompatibilityModeEnabledLocked() {
631 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800632 }
633
634 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700635 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700636 *
637 * <p>Typically used to determine whether the option to explicitly request autofill should
638 * be offered - see {@link #requestAutofill(View)}.
639 *
640 * @return whether autofill is enabled for the current user.
641 */
642 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800643 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700644 return false;
645 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700646 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800647 if (isDisabledByServiceLocked()) {
648 return false;
649 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700650 ensureServiceClientAddedIfNeededLocked();
651 return mEnabled;
652 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700653 }
654
655 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700656 * Should always be called from {@link AutofillService#getFillEventHistory()}.
657 *
658 * @hide
659 */
660 @Nullable public FillEventHistory getFillEventHistory() {
661 try {
662 return mService.getFillEventHistory();
663 } catch (RemoteException e) {
664 e.rethrowFromSystemServer();
665 return null;
666 }
667 }
668
669 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700670 * Explicitly requests a new autofill context.
671 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700672 * <p>Normally, the autofill context is automatically started if necessary when
673 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
674 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
675 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700676 *
677 * @param view view requesting the new autofill context.
678 */
679 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700680 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700681 }
682
683 /**
684 * Explicitly requests a new autofill context for virtual views.
685 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700686 * <p>Normally, the autofill context is automatically started if necessary when
687 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
688 * cases where it must be explicitly started. For example, when the virtual view offers an
689 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700690 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700691 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
692 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
693 * the absolute bounds could be calculated by:
694 *
695 * <pre class="prettyprint">
696 * int offset[] = new int[2];
697 * getLocationOnScreen(offset);
698 * Rect absBounds = new Rect(bounds.left + offset[0],
699 * bounds.top + offset[1],
700 * bounds.right + offset[0], bounds.bottom + offset[1]);
701 * </pre>
702 *
703 * @param view the virtual view parent.
704 * @param virtualId id identifying the virtual child inside the parent view.
705 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700706 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700707 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
708 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700709 }
710
Felipe Leme2ac463e2017-03-13 14:06:25 -0700711 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700712 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800713 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700714 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800715 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700716 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700717 notifyViewEntered(view, 0);
718 }
719
Andreas Gampe3f24e692018-02-05 13:24:28 -0800720 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800721 private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800722 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700723 if (sVerbose) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800724 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
725 + ") on state " + getStateAsStringLocked() + " because disabled by svc");
Felipe Leme17292d12017-10-24 14:03:10 -0700726 }
727 return true;
728 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800729 if (isFinishedLocked()) {
730 // Session already finished: ignore if automatic request and view already entered
731 if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
732 && mEnteredIds.contains(id)) {
733 if (sVerbose) {
734 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
735 + ") on state " + getStateAsStringLocked()
736 + " because view was already entered: " + mEnteredIds);
737 }
738 return true;
739 }
740 }
741 if (sVerbose) {
742 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
743 + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
Felipe Leme17292d12017-10-24 14:03:10 -0700744 }
745 return false;
746 }
747
Dake Gu67decfa2017-12-27 11:48:08 -0800748 private boolean isClientVisibleForAutofillLocked() {
749 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800750 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800751 }
752
753 private boolean isClientDisablingEnterExitEvent() {
754 final AutofillClient client = getClient();
755 return client != null && client.isDisablingEnterExitEventForAutofill();
756 }
757
Felipe Lemed1146422017-04-26 10:17:05 -0700758 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700759 if (!hasAutofillFeature()) {
760 return;
761 }
Dake Gu67decfa2017-12-27 11:48:08 -0800762 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700763 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800764 callback = notifyViewEnteredLocked(view, flags);
765 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700766
Dake Gu67decfa2017-12-27 11:48:08 -0800767 if (callback != null) {
768 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
769 }
770 }
Svet Ganov782043c2017-02-11 00:52:02 +0000771
Dake Gu67decfa2017-12-27 11:48:08 -0800772 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800773 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800774 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800775 final AutofillId id = getAutofillId(view);
776 if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
Dake Gu67decfa2017-12-27 11:48:08 -0800777
778 AutofillCallback callback = null;
779
780 ensureServiceClientAddedIfNeededLocked();
781
782 if (!mEnabled) {
783 if (mCallback != null) {
784 callback = mCallback;
785 }
786 } else {
787 // don't notify entered when Activity is already in background
788 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700789 final AutofillValue value = view.getAutofillValue();
790
Felipe Lemec24a56a2017-08-03 14:27:57 -0700791 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700792 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700793 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700794 } else {
795 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700796 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700797 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800798 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700799 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800800 }
Dake Gu67decfa2017-12-27 11:48:08 -0800801 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800802 }
803
804 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700805 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800806 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700807 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800808 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700809 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700810 if (!hasAutofillFeature()) {
811 return;
812 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700813 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800814 notifyViewExitedLocked(view);
815 }
816 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800817
Andreas Gampe3f24e692018-02-05 13:24:28 -0800818 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800819 void notifyViewExitedLocked(@NonNull View view) {
820 ensureServiceClientAddedIfNeededLocked();
821
822 if (mEnabled && isActiveLocked()) {
823 // dont notify exited when Activity is already in background
824 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700825 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800826
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700827 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700828 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700829 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800830 }
831 }
832
833 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700834 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700835 *
836 * @param view {@link View} that was exited.
837 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700838 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700839 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
840 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
841 }
842
843 /**
844 * Called when a virtual view's visibility changed.
845 *
846 * @param view {@link View} that was exited.
847 * @param virtualId id identifying the virtual child inside the parent view.
848 * @param isVisible visible if the view is visible in the view hierarchy.
849 */
850 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
851 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
852 }
853
854 /**
855 * Called when a view/virtual view's visibility changed.
856 *
857 * @param view {@link View} that was exited.
858 * @param virtualId id identifying the virtual child inside the parent view.
859 * @param isVisible visible if the view is visible in the view hierarchy.
860 * @param virtual Whether the view is virtual.
861 */
862 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
863 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700864 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700865 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700866 final AutofillId id = virtual ? getAutofillId(view, virtualId)
867 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700868 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700869 if (mFillableIds.contains(id)) {
870 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
871 requestHideFillUi(id, view);
872 }
873 }
874 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800875 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700876 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700877 }
878 }
879 }
880
881 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700882 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800883 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700884 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
885 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
886 * the absolute bounds could be calculated by:
887 *
888 * <pre class="prettyprint">
889 * int offset[] = new int[2];
890 * getLocationOnScreen(offset);
891 * Rect absBounds = new Rect(bounds.left + offset[0],
892 * bounds.top + offset[1],
893 * bounds.right + offset[0], bounds.bottom + offset[1]);
894 * </pre>
895 *
896 * @param view the virtual view parent.
897 * @param virtualId id identifying the virtual child inside the parent view.
898 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800899 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700900 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
901 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700902 }
903
Felipe Leme6dcec872017-05-25 11:24:23 -0700904 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700905 if (!hasAutofillFeature()) {
906 return;
907 }
Dake Gu67decfa2017-12-27 11:48:08 -0800908 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700909 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800910 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
911 }
Felipe Leme17292d12017-10-24 14:03:10 -0700912
Dake Gu67decfa2017-12-27 11:48:08 -0800913 if (callback != null) {
914 callback.onAutofillEvent(view, virtualId,
915 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
916 }
917 }
Svet Ganov782043c2017-02-11 00:52:02 +0000918
Dake Gu67decfa2017-12-27 11:48:08 -0800919 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800920 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800921 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
922 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800923 final AutofillId id = getAutofillId(view, virtualId);
Dake Gu67decfa2017-12-27 11:48:08 -0800924 AutofillCallback callback = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800925 if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
Dake Gu67decfa2017-12-27 11:48:08 -0800926
927 ensureServiceClientAddedIfNeededLocked();
928
929 if (!mEnabled) {
930 if (mCallback != null) {
931 callback = mCallback;
932 }
933 } else {
934 // don't notify entered when Activity is already in background
935 if (!isClientDisablingEnterExitEvent()) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700936 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700937 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700938 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700939 } else {
940 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700941 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700942 }
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800943 addEnteredIdLocked(id);
Felipe Leme24aae152017-03-15 12:33:01 -0700944 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800945 }
Dake Gu67decfa2017-12-27 11:48:08 -0800946 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800947 }
948
Felipe Leme9a15c0f2018-02-14 17:26:46 -0800949 @GuardedBy("mLock")
950 private void addEnteredIdLocked(@NonNull AutofillId id) {
951 if (mEnteredIds == null) {
952 mEnteredIds = new ArraySet<>(1);
953 }
954 mEnteredIds.add(id);
955 }
956
Philip P. Moltmann44611812017-02-23 12:52:46 -0800957 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700958 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800959 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700960 * @param view the virtual view parent.
961 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800962 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700963 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700964 if (!hasAutofillFeature()) {
965 return;
966 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700967 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800968 notifyViewExitedLocked(view, virtualId);
969 }
970 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800971
Andreas Gampe3f24e692018-02-05 13:24:28 -0800972 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800973 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
974 ensureServiceClientAddedIfNeededLocked();
975
976 if (mEnabled && isActiveLocked()) {
977 // don't notify exited when Activity is already in background
978 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700979 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800980
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700981 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700982 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700983 }
Svet Ganov782043c2017-02-11 00:52:02 +0000984 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800985 }
986
987 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800988 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800989 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800990 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800991 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700992 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700993 if (!hasAutofillFeature()) {
994 return;
995 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700996 AutofillId id = null;
997 boolean valueWasRead = false;
998 AutofillValue value = null;
999
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001000 synchronized (mLock) {
1001 // If the session is gone some fields might still be highlighted, hence we have to
1002 // remove the isAutofilled property even if no sessions are active.
1003 if (mLastAutofilledData == null) {
1004 view.setAutofilled(false);
1005 } else {
1006 id = getAutofillId(view);
1007 if (mLastAutofilledData.containsKey(id)) {
1008 value = view.getAutofillValue();
1009 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001010
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001011 if (Objects.equals(mLastAutofilledData.get(id), value)) {
1012 view.setAutofilled(true);
1013 } else {
1014 view.setAutofilled(false);
1015 mLastAutofilledData.remove(id);
1016 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001017 } else {
1018 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001019 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001020 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001021
Felipe Lemec24a56a2017-08-03 14:27:57 -07001022 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001023 if (sVerbose) {
1024 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
1025 + "): ignoring on state " + getStateAsStringLocked());
Felipe Lemec7b45292017-09-19 09:06:20 -07001026 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001027 return;
1028 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001029
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001030 if (id == null) {
1031 id = getAutofillId(view);
1032 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001033
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001034 if (!valueWasRead) {
1035 value = view.getAutofillValue();
1036 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001037
Felipe Leme0aa4c502017-04-26 12:36:01 -07001038 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001039 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001040 }
1041
Felipe Lemebab851c2017-02-03 18:45:08 -08001042 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001043 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001044 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001045 * @param view the virtual view parent.
1046 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001047 * @param value new value of the child.
1048 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001049 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001050 if (!hasAutofillFeature()) {
1051 return;
1052 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001053 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001054 if (!mEnabled || !isActiveLocked()) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001055 if (sVerbose) {
1056 Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
1057 + "): ignoring on state " + getStateAsStringLocked());
1058 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001059 return;
1060 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001061
Felipe Leme6dcec872017-05-25 11:24:23 -07001062 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001063 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001064 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001065 }
1066
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001067 /**
1068 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
1069 *
1070 * @hide
1071 */
1072 public void notifyViewClicked(View view) {
1073 final AutofillId id = view.getAutofillId();
1074
1075 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1076
1077 synchronized (mLock) {
1078 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1079 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1080 commitLocked();
1081 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
1082 mContext.getPackageName());
1083 }
1084 }
1085 }
1086
1087 /**
1088 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1089 *
1090 * @hide
1091 */
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001092 public void onActivityFinishing() {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001093 if (!hasAutofillFeature()) {
1094 return;
1095 }
1096 synchronized (mLock) {
1097 if (mSaveOnFinish) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001098 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001099 commitLocked();
1100 } else {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001101 if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001102 cancelLocked();
1103 }
1104 }
1105 }
1106
Felipe Lemebab851c2017-02-03 18:45:08 -08001107 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001108 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001109 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001110 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1111 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1112 * that represent the HTML elements, it should call this method after the form is submitted and
1113 * another page is rendered.
1114 *
1115 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1116 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001117 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001118 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001119 if (!hasAutofillFeature()) {
1120 return;
1121 }
Felipe Leme5b32ebe2018-02-15 12:52:19 -08001122 if (sVerbose) Log.v(TAG, "commit() called by app");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001123 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001124 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001125 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001126 }
1127
Andreas Gampe3f24e692018-02-05 13:24:28 -08001128 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001129 private void commitLocked() {
1130 if (!mEnabled && !isActiveLocked()) {
1131 return;
1132 }
1133 finishSessionLocked();
1134 }
1135
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001136 /**
1137 * Called to indicate the current autofill context should be cancelled.
1138 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001139 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1140 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1141 * that represent the HTML elements, it should call this method if the user does not post the
1142 * form but moves to another form in this page.
1143 *
1144 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1145 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001146 */
1147 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001148 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001149 if (!hasAutofillFeature()) {
1150 return;
1151 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001152 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001153 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001154 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001155 }
1156
Andreas Gampe3f24e692018-02-05 13:24:28 -08001157 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001158 private void cancelLocked() {
1159 if (!mEnabled && !isActiveLocked()) {
1160 return;
1161 }
1162 cancelSessionLocked();
1163 }
1164
Svet Ganovf965b872017-04-28 16:34:02 -07001165 /** @hide */
1166 public void disableOwnedAutofillServices() {
1167 disableAutofillServices();
1168 }
1169
Svet Ganovf20a0372017-04-10 17:08:05 -07001170 /**
1171 * If the app calling this API has enabled autofill services they
1172 * will be disabled.
1173 */
Svet Ganovf965b872017-04-28 16:34:02 -07001174 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001175 if (!hasAutofillFeature()) {
1176 return;
1177 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001178 try {
1179 mService.disableOwnedAutofillServices(mContext.getUserId());
1180 } catch (RemoteException e) {
1181 throw e.rethrowFromSystemServer();
1182 }
1183 }
1184
Felipe Lemedb041182017-04-21 17:33:38 -07001185 /**
1186 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1187 * enabled for the current user, or {@code false} otherwise.
1188 */
1189 public boolean hasEnabledAutofillServices() {
1190 if (mService == null) return false;
1191
1192 try {
1193 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1194 } catch (RemoteException e) {
1195 throw e.rethrowFromSystemServer();
1196 }
1197 }
1198
1199 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001200 * Returns the component name of the {@link AutofillService} that is enabled for the current
1201 * user.
1202 */
1203 @Nullable
1204 public ComponentName getAutofillServiceComponentName() {
1205 if (mService == null) return null;
1206
1207 try {
1208 return mService.getAutofillServiceComponentName();
1209 } catch (RemoteException e) {
1210 throw e.rethrowFromSystemServer();
1211 }
1212 }
1213
1214 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001215 * Gets the id of the {@link UserData} used for
1216 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1217 *
1218 * <p>This method is useful when the service must check the status of the {@link UserData} in
1219 * the device without fetching the whole object.
1220 *
1221 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1222 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1223 * the user.
1224 *
1225 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1226 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1227 * service for the user.
1228 */
1229 @Nullable public String getUserDataId() {
1230 try {
1231 return mService.getUserDataId();
1232 } catch (RemoteException e) {
1233 e.rethrowFromSystemServer();
1234 return null;
1235 }
1236 }
1237
1238 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001239 * Gets the user data used for
1240 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001241 *
Felipe Leme27f45732017-12-22 09:05:22 -08001242 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1243 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1244 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001245 *
Felipe Leme452886a2017-11-27 13:09:13 -08001246 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1247 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001248 */
Felipe Leme452886a2017-11-27 13:09:13 -08001249 @Nullable public UserData getUserData() {
1250 try {
1251 return mService.getUserData();
1252 } catch (RemoteException e) {
1253 e.rethrowFromSystemServer();
1254 return null;
1255 }
1256 }
1257
1258 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001259 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001260 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001261 *
1262 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1263 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1264 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001265 */
Felipe Leme452886a2017-11-27 13:09:13 -08001266 public void setUserData(@Nullable UserData userData) {
1267 try {
1268 mService.setUserData(userData);
1269 } catch (RemoteException e) {
1270 e.rethrowFromSystemServer();
1271 }
1272 }
1273
1274 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001275 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1276 * enabled.
1277 *
1278 * <p>As field classification is an expensive operation, it could be disabled, either
1279 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1280 * permanently (for example, because the device is a low-level device).
1281 *
1282 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1283 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1284 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001285 */
Felipe Leme329d0402017-12-06 09:22:43 -08001286 public boolean isFieldClassificationEnabled() {
1287 try {
1288 return mService.isFieldClassificationEnabled();
1289 } catch (RemoteException e) {
1290 e.rethrowFromSystemServer();
1291 return false;
1292 }
1293 }
1294
1295 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001296 * Gets the name of the default algorithm used for
1297 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1298 *
1299 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1300 * set.
1301 *
1302 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1303 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1304 * the user.
1305 */
1306 @Nullable
1307 public String getDefaultFieldClassificationAlgorithm() {
1308 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001309 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001310 } catch (RemoteException e) {
1311 e.rethrowFromSystemServer();
1312 return null;
1313 }
1314 }
1315
1316 /**
1317 * Gets the name of all algorithms currently available for
1318 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1319 *
1320 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001321 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1322 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001323 */
1324 @NonNull
1325 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001326 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001327 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001328 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1329 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001330 } catch (RemoteException e) {
1331 e.rethrowFromSystemServer();
1332 return null;
1333 }
1334 }
1335
1336 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001337 * Returns {@code true} if autofill is supported by the current device and
1338 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001339 *
1340 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1341 * <ol>
1342 * <li>Low-end devices.
1343 * <li>Device policy rules that forbid its usage.
1344 * </ol>
1345 */
1346 public boolean isAutofillSupported() {
1347 if (mService == null) return false;
1348
1349 try {
1350 return mService.isServiceSupported(mContext.getUserId());
1351 } catch (RemoteException e) {
1352 throw e.rethrowFromSystemServer();
1353 }
1354 }
1355
Felipe Leme637e05e2017-12-06 12:09:37 -08001356 // Note: don't need to use locked suffix because mContext is final.
1357 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001358 final AutofillClient client = mContext.getAutofillClient();
1359 if (client == null && sDebug) {
1360 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1361 + mContext);
1362 }
1363 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001364 }
1365
1366 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001367 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001368 if (!hasAutofillFeature()) {
1369 return;
1370 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001371 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001372 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1373 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1374 // service set the extra and returned RESULT_CANCELED...
1375
Felipe Leme9f9ee252017-04-27 13:56:22 -07001376 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001377
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001378 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001379 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001380 return;
1381 }
Dake Gu67decfa2017-12-27 11:48:08 -08001382 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1383 // of app activity. We enforce enter event to re-show fill ui in such case.
1384 // CTS example:
1385 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1386 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1387 if (!mOnInvisibleCalled && focusView != null
1388 && focusView.canNotifyAutofillEnterExitEvent()) {
1389 notifyViewExitedLocked(focusView);
1390 notifyViewEnteredLocked(focusView, 0);
1391 }
1392 if (data == null) {
1393 // data is set to null when result is not RESULT_OK
1394 return;
1395 }
1396
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001397 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1398 final Bundle responseData = new Bundle();
1399 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001400 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1401 if (newClientState != null) {
1402 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1403 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001404 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001405 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1406 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001407 } catch (RemoteException e) {
1408 Log.e(TAG, "Error delivering authentication result", e);
1409 }
Svet Ganov782043c2017-02-11 00:52:02 +00001410 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001411 }
1412
Felipe Leme640f30a2017-03-06 15:44:06 -08001413 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001414 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001415 }
1416
Felipe Leme6dcec872017-05-25 11:24:23 -07001417 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001418 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001419 }
1420
Andreas Gampe3f24e692018-02-05 13:24:28 -08001421 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001422 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1423 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001424 if (sVerbose) {
1425 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001426 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001427 + ", compatMode=" + isCompatibilityModeEnabledLocked()
1428 + ", enteredIds=" + mEnteredIds);
Felipe Lemebab851c2017-02-03 18:45:08 -08001429 }
Felipe Leme3103c632017-12-18 15:05:14 -08001430 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001431 if (sVerbose) {
1432 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001433 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001434 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001435 return;
1436 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001437 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001438 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001439 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001440
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001441 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001442 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme185de722018-02-13 17:25:44 -08001443 mCallback != null, flags, client.autofillClientGetComponentName(),
1444 isCompatibilityModeEnabledLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001445 if (mSessionId != NO_SESSION) {
1446 mState = STATE_ACTIVE;
1447 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001448 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001449 } catch (RemoteException e) {
1450 throw e.rethrowFromSystemServer();
1451 }
1452 }
1453
Andreas Gampe3f24e692018-02-05 13:24:28 -08001454 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001455 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001456 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001457
1458 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001459
Svet Ganov782043c2017-02-11 00:52:02 +00001460 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001461 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001462 } catch (RemoteException e) {
1463 throw e.rethrowFromSystemServer();
1464 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001465
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001466 resetSessionLocked(/* resetEnteredIds= */ true);
Felipe Lemebab851c2017-02-03 18:45:08 -08001467 }
1468
Andreas Gampe3f24e692018-02-05 13:24:28 -08001469 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001470 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001471 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001472
1473 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001474
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001475 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001476 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001477 } catch (RemoteException e) {
1478 throw e.rethrowFromSystemServer();
1479 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001480
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001481 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001482 }
1483
Andreas Gampe3f24e692018-02-05 13:24:28 -08001484 @GuardedBy("mLock")
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001485 private void resetSessionLocked(boolean resetEnteredIds) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001486 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001487 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001488 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001489 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001490 mSaveTriggerId = null;
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001491 if (resetEnteredIds) {
1492 mEnteredIds = null;
1493 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001494 }
1495
Andreas Gampe3f24e692018-02-05 13:24:28 -08001496 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001497 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1498 int flags) {
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001499 if (sVerbose) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001500 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1501 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001502 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001503 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1504
Felipe Leme3461d3c2017-01-19 08:54:55 -08001505 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001506 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001507 final AutofillClient client = getClient();
1508 if (client == null) return; // NOTE: getClient() already logd it..
1509
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001510 final int newId = mService.updateOrRestartSession(
1511 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001512 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001513 mCallback != null, flags, client.autofillClientGetComponentName(),
Felipe Leme185de722018-02-13 17:25:44 -08001514 mSessionId, action, isCompatibilityModeEnabledLocked());
Felipe Leme7f33cd32017-05-11 10:10:49 -07001515 if (newId != mSessionId) {
1516 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1517 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001518 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001519 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001520 }
1521 } else {
1522 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1523 mContext.getUserId());
1524 }
1525
Felipe Leme3461d3c2017-01-19 08:54:55 -08001526 } catch (RemoteException e) {
1527 throw e.rethrowFromSystemServer();
1528 }
1529 }
Svet Ganov782043c2017-02-11 00:52:02 +00001530
Andreas Gampe3f24e692018-02-05 13:24:28 -08001531 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001532 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001533 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001534 return;
1535 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001536
Svet Ganov782043c2017-02-11 00:52:02 +00001537 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001538 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001539 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001540 final int userId = mContext.getUserId();
1541 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001542 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1543 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1544 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001545 final IAutoFillManager service = mService;
1546 final IAutoFillManagerClient serviceClient = mServiceClient;
1547 mServiceClientCleaner = Cleaner.create(this, () -> {
1548 try {
1549 service.removeClient(serviceClient, userId);
1550 } catch (RemoteException e) {
1551 }
1552 });
Svet Ganov782043c2017-02-11 00:52:02 +00001553 } catch (RemoteException e) {
1554 throw e.rethrowFromSystemServer();
1555 }
1556 }
1557 }
1558
Felipe Lemee6010f22017-03-03 11:19:51 -08001559 /**
1560 * Registers a {@link AutofillCallback} to receive autofill events.
1561 *
1562 * @param callback callback to receive events.
1563 */
1564 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001565 if (!hasAutofillFeature()) {
1566 return;
1567 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001568 synchronized (mLock) {
1569 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001570
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001571 final boolean hadCallback = mCallback != null;
1572 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001573
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001574 if (!hadCallback) {
1575 try {
1576 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1577 } catch (RemoteException e) {
1578 throw e.rethrowFromSystemServer();
1579 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001580 }
1581 }
1582 }
1583
1584 /**
1585 * Unregisters a {@link AutofillCallback} to receive autofill events.
1586 *
1587 * @param callback callback to stop receiving events.
1588 */
1589 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001590 if (!hasAutofillFeature()) {
1591 return;
1592 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001593 synchronized (mLock) {
1594 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001595
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001596 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001597
Felipe Lemee6010f22017-03-03 11:19:51 -08001598 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001599 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001600 } catch (RemoteException e) {
1601 throw e.rethrowFromSystemServer();
1602 }
1603 }
1604 }
1605
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001606 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1607 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1608 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001609 if (anchor == null) {
1610 return;
1611 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001612
1613 AutofillCallback callback = null;
1614 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001615 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001616 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001617
1618 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001619 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001620 anchorBounds, presenter) && mCallback != null) {
1621 callback = mCallback;
1622 }
1623 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001624 }
1625 }
1626
1627 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001628 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001629 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001630 AutofillCallback.EVENT_INPUT_SHOWN);
1631 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001632 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001633 }
1634 }
1635 }
1636
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001637 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1638 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001639 synchronized (mLock) {
1640 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001641 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001642 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001643 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1644 // before onAuthenticationResult()
1645 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001646 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001647 }
1648 }
1649 }
1650 }
1651
Felipe Leme51e29da2017-10-24 14:03:10 -07001652 /** @hide */
1653 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1654 /** @hide */
1655 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1656 /** @hide */
1657 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1658 /** @hide */
1659 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1660 /** @hide */
1661 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1662
1663 private void setState(int flags) {
1664 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001665 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001666 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1667 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001668 // Reset the session state
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001669 resetSessionLocked(/* resetEnteredIds= */ true);
Svet Ganov48f10a22017-04-26 18:49:30 -07001670 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001671 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001672 // Reset connection to system
1673 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001674 if (mServiceClientCleaner != null) {
1675 mServiceClientCleaner.clean();
1676 mServiceClientCleaner = null;
1677 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001678 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001679 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001680 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1681 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001682 }
1683
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001684 /**
1685 * Sets a view as autofilled if the current value is the {code targetValue}.
1686 *
1687 * @param view The view that is to be autofilled
1688 * @param targetValue The value we want to fill into view
1689 */
1690 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1691 AutofillValue currentValue = view.getAutofillValue();
1692 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001693 synchronized (mLock) {
1694 if (mLastAutofilledData == null) {
1695 mLastAutofilledData = new ParcelableMap(1);
1696 }
1697 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001698 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001699 view.setAutofilled(true);
1700 }
1701 }
1702
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001703 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001704 synchronized (mLock) {
1705 if (sessionId != mSessionId) {
1706 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001707 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001708
Felipe Leme637e05e2017-12-06 12:09:37 -08001709 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001710 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001711 return;
1712 }
1713
1714 final int itemCount = ids.size();
1715 int numApplied = 0;
1716 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001717 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1718 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001719
1720 for (int i = 0; i < itemCount; i++) {
1721 final AutofillId id = ids.get(i);
1722 final AutofillValue value = values.get(i);
1723 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001724 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001725 if (view == null) {
1726 Log.w(TAG, "autofill(): no View with id " + viewId);
1727 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001728 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001729 if (id.isVirtual()) {
1730 if (virtualValues == null) {
1731 // Most likely there will be just one view with virtual children.
1732 virtualValues = new ArrayMap<>(1);
1733 }
1734 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1735 if (valuesByParent == null) {
1736 // We don't know the size yet, but usually it will be just a few fields...
1737 valuesByParent = new SparseArray<>(5);
1738 virtualValues.put(view, valuesByParent);
1739 }
1740 valuesByParent.put(id.getVirtualChildId(), value);
1741 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001742 // Mark the view as to be autofilled with 'value'
1743 if (mLastAutofilledData == null) {
1744 mLastAutofilledData = new ParcelableMap(itemCount - i);
1745 }
1746 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001747
1748 view.autofill(value);
1749
1750 // Set as autofilled if the values match now, e.g. when the value was updated
1751 // synchronously.
1752 // If autofill happens async, the view is set to autofilled in
1753 // notifyValueChanged.
1754 setAutofilledIfValuesIs(view, value);
1755
1756 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001757 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001758 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001759
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001760 if (virtualValues != null) {
1761 for (int i = 0; i < virtualValues.size(); i++) {
1762 final View parent = virtualValues.keyAt(i);
1763 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1764 parent.autofill(childrenValues);
1765 numApplied += childrenValues.size();
1766 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001767 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001768
Felipe Lemeb22d6352017-09-08 20:03:53 -07001769 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1770 .setPackageName(mContext.getPackageName())
1771 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1772 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001773 mMetricsLogger.write(log);
1774 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001775 }
1776
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001777 /**
1778 * Set the tracked views.
1779 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001780 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001781 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001782 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001783 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001784 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001785 */
Felipe Leme27e20222017-05-18 15:24:11 -07001786 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001787 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1788 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001789 synchronized (mLock) {
1790 if (mEnabled && mSessionId == sessionId) {
1791 if (saveOnAllViewsInvisible) {
1792 mTrackedViews = new TrackedViews(trackedIds);
1793 } else {
1794 mTrackedViews = null;
1795 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001796 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001797 if (fillableIds != null) {
1798 if (mFillableIds == null) {
1799 mFillableIds = new ArraySet<>(fillableIds.length);
1800 }
1801 for (AutofillId id : fillableIds) {
1802 mFillableIds.add(id);
1803 }
1804 if (sVerbose) {
1805 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1806 + ", mFillableIds" + mFillableIds);
1807 }
1808 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001809
1810 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1811 // Turn off trigger on previous view id.
1812 setNotifyOnClickLocked(mSaveTriggerId, false);
1813 }
1814
1815 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1816 // Turn on trigger on new view id.
1817 mSaveTriggerId = saveTriggerId;
1818 setNotifyOnClickLocked(mSaveTriggerId, true);
1819 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001820 }
1821 }
1822 }
1823
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001824 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1825 final View view = findView(id);
1826 if (view == null) {
1827 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1828 return;
1829 }
1830 view.setNotifyAutofillManagerOnClick(notify);
1831 }
1832
Felipe Lemec24a56a2017-08-03 14:27:57 -07001833 private void setSaveUiState(int sessionId, boolean shown) {
1834 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1835 synchronized (mLock) {
1836 if (mSessionId != NO_SESSION) {
1837 // Race condition: app triggered a new session after the previous session was
1838 // finished but before server called setSaveUiState() - need to cancel the new
1839 // session to avoid further inconsistent behavior.
1840 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1841 + ") called on existing session " + mSessionId + "; cancelling it");
1842 cancelSessionLocked();
1843 }
1844 if (shown) {
1845 mSessionId = sessionId;
1846 mState = STATE_SHOWING_SAVE_UI;
1847 } else {
1848 mSessionId = NO_SESSION;
1849 mState = STATE_UNKNOWN;
1850 }
1851 }
1852 }
1853
Felipe Leme650f7ab2017-09-19 13:08:24 -07001854 /**
1855 * Marks the state of the session as finished.
1856 *
1857 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001858 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1859 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1860 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001861 */
1862 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001863 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001864 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001865 resetSessionLocked(/* resetEnteredIds= */ false);
Felipe Leme650f7ab2017-09-19 13:08:24 -07001866 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001867 }
1868 }
1869
Felipe Leme27e20222017-05-18 15:24:11 -07001870 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001871 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001872 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001873 if (anchor == null) {
1874 return;
1875 }
Felipe Leme27e20222017-05-18 15:24:11 -07001876 requestHideFillUi(id, anchor);
1877 }
1878
1879 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001880
1881 AutofillCallback callback = null;
1882 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001883 // We do not check the session id for two reasons:
1884 // 1. If local and remote session id are off sync the UI would be stuck shown
1885 // 2. There is a race between the user state being destroyed due the fill
1886 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001887 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001888 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001889 if (client.autofillClientRequestHideFillUi() && mCallback != null) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001890 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001891 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001892 }
1893 }
1894
1895 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001896 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001897 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001898 AutofillCallback.EVENT_INPUT_HIDDEN);
1899 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001900 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001901 }
1902 }
1903 }
1904
Felipe Leme17292d12017-10-24 14:03:10 -07001905 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001906 if (sVerbose) {
1907 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001908 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001909 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001910 final View anchor = findView(id);
1911 if (anchor == null) {
1912 return;
1913 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001914
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001915 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001916 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001917 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001918 callback = mCallback;
1919 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001920 }
1921
1922 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001923 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001924 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001925 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1926 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001927 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001928 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001929 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001930
Felipe Leme17292d12017-10-24 14:03:10 -07001931 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001932 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001933 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001934 }
1935 }
1936
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001937 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001938 * Find a single view by its id.
1939 *
1940 * @param autofillId The autofill id of the view
1941 *
1942 * @return The view or {@code null} if view was not found
1943 */
1944 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001945 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001946 if (client != null) {
1947 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08001948 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001949 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001950 }
1951
Felipe Lemebb810922017-04-25 15:54:06 -07001952 /** @hide */
1953 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001954 return mService != null;
1955 }
1956
Felipe Lemec24a56a2017-08-03 14:27:57 -07001957 /** @hide */
1958 public void onPendingSaveUi(int operation, IBinder token) {
1959 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1960
1961 synchronized (mLock) {
1962 try {
1963 mService.onPendingSaveUi(operation, token);
1964 } catch (RemoteException e) {
1965 e.rethrowFromSystemServer();
1966 }
1967 }
1968 }
1969
1970 /** @hide */
1971 public void dump(String outerPrefix, PrintWriter pw) {
1972 pw.print(outerPrefix); pw.println("AutofillManager:");
1973 final String pfx = outerPrefix + " ";
1974 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001975 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001976 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001977 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001978 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1979 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1980 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08001981 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001982 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1983 pw.print(pfx); pw.print("tracked views: ");
1984 if (mTrackedViews == null) {
1985 pw.println("null");
1986 } else {
1987 final String pfx2 = pfx + " ";
1988 pw.println();
1989 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1990 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1991 }
1992 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme9a15c0f2018-02-14 17:26:46 -08001993 pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001994 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1995 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
felipealfd3f8f62018-02-07 11:49:07 +01001996 pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
1997 isCompatibilityModeEnabledLocked());
Felipe Leme51e29da2017-10-24 14:03:10 -07001998 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1999 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07002000 }
2001
Andreas Gampe3f24e692018-02-05 13:24:28 -08002002 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07002003 private String getStateAsStringLocked() {
2004 switch (mState) {
2005 case STATE_UNKNOWN:
2006 return "STATE_UNKNOWN";
2007 case STATE_ACTIVE:
2008 return "STATE_ACTIVE";
2009 case STATE_FINISHED:
2010 return "STATE_FINISHED";
2011 case STATE_SHOWING_SAVE_UI:
2012 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07002013 case STATE_DISABLED_BY_SERVICE:
2014 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07002015 default:
2016 return "INVALID:" + mState;
2017 }
2018 }
2019
Andreas Gampe3f24e692018-02-05 13:24:28 -08002020 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07002021 private boolean isActiveLocked() {
2022 return mState == STATE_ACTIVE;
2023 }
2024
Andreas Gampe3f24e692018-02-05 13:24:28 -08002025 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002026 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07002027 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07002028 }
2029
Andreas Gampe3f24e692018-02-05 13:24:28 -08002030 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08002031 private boolean isFinishedLocked() {
2032 return mState == STATE_FINISHED;
2033 }
2034
Felipe Leme9876a6f2017-05-30 15:47:28 -07002035 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002036 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07002037 if (client == null) {
2038 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2039 return;
2040 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002041 client.autofillClientRunOnUiThread(runnable);
2042 }
2043
2044 /**
2045 * Implementation of the accessibility based compatibility.
2046 */
2047 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2048 @GuardedBy("mLock")
2049 private final Rect mFocusedBounds = new Rect();
2050 @GuardedBy("mLock")
2051 private final Rect mTempBounds = new Rect();
2052
2053 @GuardedBy("mLock")
2054 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2055 @GuardedBy("mLock")
2056 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2057
2058 // Need to report a fake service in case a11y clients check the service list
2059 @NonNull
2060 @GuardedBy("mLock")
2061 AccessibilityServiceInfo mCompatServiceInfo;
2062
2063 CompatibilityBridge() {
2064 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2065 am.setAccessibilityPolicy(this);
2066 }
2067
2068 private AccessibilityServiceInfo getCompatServiceInfo() {
2069 synchronized (mLock) {
2070 if (mCompatServiceInfo != null) {
2071 return mCompatServiceInfo;
2072 }
2073 final Intent intent = new Intent();
2074 intent.setComponent(new ComponentName("android",
2075 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2076 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2077 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2078 try {
2079 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2080 } catch (XmlPullParserException | IOException e) {
2081 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2082 throw new IllegalStateException("Cannot find compat autofill service");
2083 }
2084 return mCompatServiceInfo;
2085 }
2086 }
2087
2088 @Override
2089 public boolean isEnabled(boolean accessibilityEnabled) {
2090 return true;
2091 }
2092
2093 @Override
2094 public int getRelevantEventTypes(int relevantEventTypes) {
2095 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2096 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
2097 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2098 }
2099
2100 @Override
2101 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2102 List<AccessibilityServiceInfo> installedServices) {
2103 if (installedServices == null) {
2104 installedServices = new ArrayList<>();
2105 }
2106 installedServices.add(getCompatServiceInfo());
2107 return installedServices;
2108 }
2109
2110 @Override
2111 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2112 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2113 if (enabledService == null) {
2114 enabledService = new ArrayList<>();
2115 }
2116 enabledService.add(getCompatServiceInfo());
2117 return enabledService;
2118 }
2119
2120 @Override
2121 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2122 boolean accessibilityEnabled, int relevantEventTypes) {
2123 switch (event.getEventType()) {
2124 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2125 synchronized (mLock) {
2126 if (mFocusedWindowId == event.getWindowId()
2127 && mFocusedNodeId == event.getSourceNodeId()) {
2128 return event;
2129 }
2130 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2131 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2132 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2133 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2134 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2135 mFocusedBounds.set(0, 0, 0, 0);
2136 }
2137 final int windowId = event.getWindowId();
2138 final long nodeId = event.getSourceNodeId();
2139 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2140 mFocusedWindowId = windowId;
2141 mFocusedNodeId = nodeId;
2142 }
2143 }
2144 } break;
2145
2146 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2147 synchronized (mLock) {
2148 if (mFocusedWindowId == event.getWindowId()
2149 && mFocusedNodeId == event.getSourceNodeId()) {
2150 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2151 }
2152 }
2153 } break;
2154
2155 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2156 final AutofillClient client = getClient();
2157 if (client != null) {
2158 synchronized (mLock) {
2159 if (client.autofillClientIsFillUiShowing()) {
2160 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2161 }
2162 updateTrackedViewsLocked();
2163 }
2164 }
2165 } break;
2166 }
2167
2168 return accessibilityEnabled ? event : null;
2169 }
2170
2171 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2172 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2173 if (!isVirtualNode(virtualId)) {
2174 return false;
2175 }
2176 final View view = findViewByAccessibilityId(windowId, nodeId);
2177 if (view == null) {
2178 return false;
2179 }
2180 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2181 if (node == null) {
2182 return false;
2183 }
2184 if (!node.isEditable()) {
2185 return false;
2186 }
2187 final Rect newBounds = mTempBounds;
2188 node.getBoundsInScreen(newBounds);
2189 if (newBounds.equals(focusedBounds)) {
2190 return false;
2191 }
2192 focusedBounds.set(newBounds);
2193 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2194 return true;
2195 }
2196
2197 private void notifyViewExited(int windowId, long nodeId) {
2198 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2199 if (!isVirtualNode(virtualId)) {
2200 return;
2201 }
2202 final View view = findViewByAccessibilityId(windowId, nodeId);
2203 if (view == null) {
2204 return;
2205 }
2206 AutofillManager.this.notifyViewExited(view, virtualId);
2207 }
2208
2209 private void notifyValueChanged(int windowId, long nodeId) {
2210 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2211 if (!isVirtualNode(virtualId)) {
2212 return;
2213 }
2214 final View view = findViewByAccessibilityId(windowId, nodeId);
2215 if (view == null) {
2216 return;
2217 }
2218 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2219 if (node == null) {
2220 return;
2221 }
2222 AutofillManager.this.notifyValueChanged(view, virtualId,
2223 AutofillValue.forText(node.getText()));
2224 }
2225
Andreas Gampe3f24e692018-02-05 13:24:28 -08002226 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002227 private void updateTrackedViewsLocked() {
2228 if (mTrackedViews != null) {
2229 mTrackedViews.onVisibleForAutofillChangedLocked();
2230 }
2231 }
2232
2233 private View findViewByAccessibilityId(int windowId, long nodeId) {
2234 final AutofillClient client = getClient();
2235 if (client == null) {
2236 return null;
2237 }
2238 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2239 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2240 }
2241
2242 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2243 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2244 if (provider == null) {
2245 return null;
2246 }
2247 return provider.createAccessibilityNodeInfo(virtualId);
2248 }
2249
2250 private boolean isVirtualNode(int nodeId) {
2251 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2252 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2253 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002254 }
2255
Felipe Lemee6010f22017-03-03 11:19:51 -08002256 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002257 * View tracking information. Once all tracked views become invisible the session is finished.
2258 */
2259 private class TrackedViews {
2260 /** Visible tracked views */
2261 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2262
2263 /** Invisible tracked views */
2264 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2265
2266 /**
2267 * Check if set is null or value is in set.
2268 *
2269 * @param set The set or null (== empty set)
2270 * @param value The value that might be in the set
2271 *
2272 * @return {@code true} iff set is not empty and value is in set
2273 */
Felipe Leme27e20222017-05-18 15:24:11 -07002274 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002275 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2276 return set != null && set.contains(value);
2277 }
2278
2279 /**
2280 * Add a value to a set. If set is null, create a new set.
2281 *
2282 * @param set The set or null (== empty set)
2283 * @param valueToAdd The value to add
2284 *
2285 * @return The set including the new value. If set was {@code null}, a set containing only
2286 * the new value.
2287 */
Felipe Leme27e20222017-05-18 15:24:11 -07002288 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002289 @NonNull
2290 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2291 if (set == null) {
2292 set = new ArraySet<>(1);
2293 }
2294
2295 set.add(valueToAdd);
2296
2297 return set;
2298 }
2299
2300 /**
2301 * Remove a value from a set.
2302 *
2303 * @param set The set or null (== empty set)
2304 * @param valueToRemove The value to remove
2305 *
2306 * @return The set without the removed value. {@code null} if set was null, or is empty
2307 * after removal.
2308 */
Felipe Leme27e20222017-05-18 15:24:11 -07002309 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002310 @Nullable
2311 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2312 if (set == null) {
2313 return null;
2314 }
2315
2316 set.remove(valueToRemove);
2317
2318 if (set.isEmpty()) {
2319 return null;
2320 }
2321
2322 return set;
2323 }
2324
2325 /**
2326 * Set the tracked views.
2327 *
2328 * @param trackedIds The views to be tracked
2329 */
Felipe Leme27e20222017-05-18 15:24:11 -07002330 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002331 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002332 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002333 final boolean[] isVisible;
2334
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002335 if (client.autofillClientIsVisibleForAutofill()) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002336 if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002337 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002338 } else {
2339 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002340 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002341 }
2342
Felipe Leme27e20222017-05-18 15:24:11 -07002343 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002344 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002345 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002346
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002347 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002348 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002349 } else {
2350 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2351 }
2352 }
2353 }
2354
Felipe Leme9f9ee252017-04-27 13:56:22 -07002355 if (sVerbose) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002356 Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002357 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2358 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2359 }
2360
2361 if (mVisibleTrackedIds == null) {
2362 finishSessionLocked();
2363 }
2364 }
2365
2366 /**
2367 * Called when a {@link View view's} visibility changes.
2368 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002369 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002370 * @param isVisible visible if the view is visible in the view hierarchy.
2371 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002372 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002373 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002374 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002375 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002376 + isVisible);
2377 }
2378
Dake Gu67decfa2017-12-27 11:48:08 -08002379 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002380 if (isVisible) {
2381 if (isInSet(mInvisibleTrackedIds, id)) {
2382 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2383 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2384 }
2385 } else {
2386 if (isInSet(mVisibleTrackedIds, id)) {
2387 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2388 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2389 }
2390 }
2391 }
2392
2393 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002394 if (sVerbose) {
2395 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2396 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002397 finishSessionLocked();
2398 }
2399 }
2400
2401 /**
2402 * Called once the client becomes visible.
2403 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002404 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002405 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002406 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002407 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002408 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002409 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002410 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002411 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2412 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2413 if (client != null) {
2414 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002415 final ArrayList<AutofillId> orderedInvisibleIds =
2416 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002417 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2418 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002419
2420 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2421 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2422 final AutofillId id = orderedInvisibleIds.get(i);
2423 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002424 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2425
Felipe Leme9f9ee252017-04-27 13:56:22 -07002426 if (sDebug) {
2427 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002428 }
2429 } else {
2430 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2431 }
2432 }
2433 }
2434
2435 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002436 final ArrayList<AutofillId> orderedVisibleIds =
2437 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002438 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2439 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002440
2441 final int numVisibleTrackedIds = orderedVisibleIds.size();
2442 for (int i = 0; i < numVisibleTrackedIds; i++) {
2443 final AutofillId id = orderedVisibleIds.get(i);
2444
2445 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002446 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2447 } else {
2448 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2449
Felipe Leme9f9ee252017-04-27 13:56:22 -07002450 if (sDebug) {
2451 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002452 }
2453 }
2454 }
2455 }
2456
2457 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2458 mVisibleTrackedIds = updatedVisibleTrackedIds;
2459 }
2460
2461 if (mVisibleTrackedIds == null) {
Felipe Leme5b32ebe2018-02-15 12:52:19 -08002462 if (sVerbose) {
2463 Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
2464 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002465 finishSessionLocked();
2466 }
2467 }
2468 }
2469
2470 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002471 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002472 *
2473 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002474 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002475 */
2476 public abstract static class AutofillCallback {
2477
2478 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002479 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2480 EVENT_INPUT_SHOWN,
2481 EVENT_INPUT_HIDDEN,
2482 EVENT_INPUT_UNAVAILABLE
2483 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002484 @Retention(RetentionPolicy.SOURCE)
2485 public @interface AutofillEventType {}
2486
2487 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002488 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002489 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002490 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002491 * should be hidden upon receiving this event.
2492 */
2493 public static final int EVENT_INPUT_SHOWN = 1;
2494
2495 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002496 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002497 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002498 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002499 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2500 */
2501 public static final int EVENT_INPUT_HIDDEN = 2;
2502
2503 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002504 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002505 * autofill is not available.
2506 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002507 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002508 * to avoid flickering, it could shown it upon receiving this event.
2509 */
2510 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2511
2512 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002513 * Called after a change in the autofill state associated with a view.
2514 *
2515 * @param view view associated with the change.
2516 *
2517 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2518 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002519 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2520 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002521
2522 /**
2523 * Called after a change in the autofill state associated with a virtual view.
2524 *
2525 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002526 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002527 *
2528 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2529 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002530 public void onAutofillEvent(@NonNull View view, int virtualId,
2531 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002532 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002533 }
2534
Felipe Leme640f30a2017-03-06 15:44:06 -08002535 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2536 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002537
Felipe Leme640f30a2017-03-06 15:44:06 -08002538 AutofillManagerClient(AutofillManager autofillManager) {
2539 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002540 }
2541
2542 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002543 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002544 final AutofillManager afm = mAfm.get();
2545 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002546 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002547 }
2548 }
2549
2550 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002551 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002552 final AutofillManager afm = mAfm.get();
2553 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002554 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002555 }
2556 }
2557
2558 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002559 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2560 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002561 final AutofillManager afm = mAfm.get();
2562 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002563 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002564 }
2565 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002566
2567 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002568 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2569 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002570 final AutofillManager afm = mAfm.get();
2571 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002572 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2573 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002574 }
2575 }
2576
2577 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002578 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002579 final AutofillManager afm = mAfm.get();
2580 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002581 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002582 }
2583 }
2584
2585 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002586 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002587 final AutofillManager afm = mAfm.get();
2588 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002589 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002590 }
2591 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002592
2593 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002594 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002595 final AutofillManager afm = mAfm.get();
2596 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002597 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002598 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002599 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002600 } catch (IntentSender.SendIntentException e) {
2601 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2602 }
2603 });
2604 }
2605 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002606
2607 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002608 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002609 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2610 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002611 final AutofillManager afm = mAfm.get();
2612 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002613 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2614 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002615 }
2616 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002617
2618 @Override
2619 public void setSaveUiState(int sessionId, boolean shown) {
2620 final AutofillManager afm = mAfm.get();
2621 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002622 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2623 }
2624 }
2625
2626 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002627 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002628 final AutofillManager afm = mAfm.get();
2629 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002630 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002631 }
2632 }
Svet Ganov782043c2017-02-11 00:52:02 +00002633 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002634}