blob: 5131a8ad4bf016044d85e946fe92957a6bb6773a [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 Leme2fe3ade2017-09-28 15:03:36 -0700359 /** If set, session is commited when the field is clicked. */
360 @GuardedBy("mLock")
361 @Nullable private AutofillId mSaveTriggerId;
362
Dake Gu67decfa2017-12-27 11:48:08 -0800363 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
364 @GuardedBy("mLock")
365 private boolean mOnInvisibleCalled;
366
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700367 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
368 @GuardedBy("mLock")
369 private boolean mSaveOnFinish;
370
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800371 /** If compatibility mode is enabled - this is a bridge to interact with a11y */
372 @GuardedBy("mLock")
373 private CompatibilityBridge mCompatibilityBridge;
374
Svet Ganov782043c2017-02-11 00:52:02 +0000375 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800376 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000377 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000378 * Asks the client to start an authentication flow.
379 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700380 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000381 * @param intent The authentication intent.
382 * @param fillInIntent The authentication fill-in intent.
383 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800384 void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700385 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800386
387 /**
388 * Tells the client this manager has state to be reset.
389 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800390 void autofillClientResetableStateAvailable();
Felipe Leme4753bb02017-03-22 20:24:00 -0700391
392 /**
393 * Request showing the autofill UI.
394 *
395 * @param anchor The real view the UI needs to anchor to.
396 * @param width The width of the fill UI content.
397 * @param height The height of the fill UI content.
398 * @param virtualBounds The bounds of the virtual decendant of the anchor.
399 * @param presenter The presenter that controls the fill UI window.
400 * @return Whether the UI was shown.
401 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800402 boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
Felipe Leme4753bb02017-03-22 20:24:00 -0700403 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
404
405 /**
406 * Request hiding the autofill UI.
407 *
408 * @return Whether the UI was hidden.
409 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800410 boolean autofillClientRequestHideFillUi();
411
412 /**
413 * Gets whether the fill UI is currenlty being shown.
414 *
415 * @return Whether the fill UI is currently being shown
416 */
417 boolean autofillClientIsFillUiShowing();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700418
419 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700420 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700421 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700422 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700423 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800424 @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700425
426 /**
427 * Checks is the client is currently visible as understood by autofill.
428 *
429 * @return {@code true} if the client is currently visible
430 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800431 boolean autofillClientIsVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700432
433 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800434 * Client might disable enter/exit event e.g. when activity is paused.
435 */
436 boolean isDisablingEnterExitEventForAutofill();
437
438 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700439 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700440 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800441 * @param autofillIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700442 *
Felipe Leme27e20222017-05-18 15:24:11 -0700443 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700444 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800445 @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
446 @NonNull AutofillId[] autofillIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700447
448 /**
449 * Finds a view by traversing the hierarchies of the client.
450 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800451 * @param autofillId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700452 *
453 * @return The view, or {@code null} if not found
454 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800455 @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
456
457 /**
458 * Finds a view by a11y id in a given client window.
459 *
460 * @param viewId The accessibility id of the views to find
461 * @param windowId The accessibility window id where to search
462 *
463 * @return The view, or {@code null} if not found
464 */
465 @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700466
467 /**
468 * Runs the specified action on the UI thread.
469 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800470 void autofillClientRunOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700471
472 /**
473 * Gets the complete component name of this client.
474 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800475 ComponentName autofillClientGetComponentName();
476
477 /**
478 * Gets the activity token
479 */
480 @Nullable IBinder autofillClientGetActivityToken();
481
482 /**
483 * @return Whether compatibility mode is enabled.
484 */
485 boolean autofillIsCompatibilityModeEnabled();
Svet Ganov782043c2017-02-11 00:52:02 +0000486 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800487
488 /**
489 * @hide
490 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800491 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800492 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800493 mService = service;
494 }
495
496 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800497 * @hide
498 */
499 public void enableCompatibilityMode() {
500 synchronized (mLock) {
501 // The accessibility manager is a singleton so we may need to plug
502 // different bridge based on which activity is currently focused
503 // in the current process. Since compat would be rarely used, just
504 // create and register a new instance every time.
505 mCompatibilityBridge = new CompatibilityBridge();
506 }
507 }
508
509 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700510 * Restore state after activity lifecycle
511 *
512 * @param savedInstanceState The state to be restored
513 *
514 * {@hide}
515 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700516 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700517 if (!hasAutofillFeature()) {
518 return;
519 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700520 synchronized (mLock) {
521 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
522
Felipe Lemec24a56a2017-08-03 14:27:57 -0700523 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700524 Log.w(TAG, "New session was started before onCreate()");
525 return;
526 }
527
528 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700529 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700530
531 if (mSessionId != NO_SESSION) {
532 ensureServiceClientAddedIfNeededLocked();
533
Felipe Leme637e05e2017-12-06 12:09:37 -0800534 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700535 if (client != null) {
536 try {
537 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800538 client.autofillClientGetActivityToken(),
539 mServiceClient.asBinder());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700540
541 if (!sessionWasRestored) {
542 Log.w(TAG, "Session " + mSessionId + " could not be restored");
543 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700544 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700545 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700546 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700547 Log.d(TAG, "session " + mSessionId + " was restored");
548 }
549
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800550 client.autofillClientResetableStateAvailable();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700551 }
552 } catch (RemoteException e) {
553 Log.e(TAG, "Could not figure out if there was an autofill session", e);
554 }
555 }
556 }
557 }
558 }
559
560 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700561 * Called once the client becomes visible.
562 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800563 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700564 *
565 * {@hide}
566 */
567 public void onVisibleForAutofill() {
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800568 // This gets called when the client just got visible at which point the visibility
569 // of the tracked views may not have been computed (due to a pending layout, etc).
570 // While generally we have no way to know when the UI has settled. We will evaluate
571 // the tracked views state at the end of next frame to guarantee that everything
572 // that may need to be laid out is laid out.
573 Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
574 synchronized (mLock) {
575 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
576 mTrackedViews.onVisibleForAutofillChangedLocked();
577 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700578 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800579 }, null);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700580 }
581
582 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800583 * Called once the client becomes invisible.
584 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800585 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Dake Gu67decfa2017-12-27 11:48:08 -0800586 *
587 * {@hide}
588 */
589 public void onInvisibleForAutofill() {
590 synchronized (mLock) {
591 mOnInvisibleCalled = true;
592 }
593 }
594
595 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700596 * Save state before activity lifecycle
597 *
598 * @param outState Place to store the state
599 *
600 * {@hide}
601 */
602 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700603 if (!hasAutofillFeature()) {
604 return;
605 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700606 synchronized (mLock) {
607 if (mSessionId != NO_SESSION) {
608 outState.putInt(SESSION_ID_TAG, mSessionId);
609 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700610 if (mState != STATE_UNKNOWN) {
611 outState.putInt(STATE_TAG, mState);
612 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700613 if (mLastAutofilledData != null) {
614 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
615 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700616 }
617 }
618
619 /**
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800620 * @hide
621 */
felipealfd3f8f62018-02-07 11:49:07 +0100622 public boolean isCompatibilityModeEnabledLocked() {
623 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800624 }
625
626 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700627 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700628 *
629 * <p>Typically used to determine whether the option to explicitly request autofill should
630 * be offered - see {@link #requestAutofill(View)}.
631 *
632 * @return whether autofill is enabled for the current user.
633 */
634 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800635 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700636 return false;
637 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700638 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800639 if (isDisabledByServiceLocked()) {
640 return false;
641 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700642 ensureServiceClientAddedIfNeededLocked();
643 return mEnabled;
644 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700645 }
646
647 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700648 * Should always be called from {@link AutofillService#getFillEventHistory()}.
649 *
650 * @hide
651 */
652 @Nullable public FillEventHistory getFillEventHistory() {
653 try {
654 return mService.getFillEventHistory();
655 } catch (RemoteException e) {
656 e.rethrowFromSystemServer();
657 return null;
658 }
659 }
660
661 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700662 * Explicitly requests a new autofill context.
663 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700664 * <p>Normally, the autofill context is automatically started if necessary when
665 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
666 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
667 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700668 *
669 * @param view view requesting the new autofill context.
670 */
671 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700672 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700673 }
674
675 /**
676 * Explicitly requests a new autofill context for virtual views.
677 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700678 * <p>Normally, the autofill context is automatically started if necessary when
679 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
680 * cases where it must be explicitly started. For example, when the virtual view offers an
681 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700682 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700683 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
684 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
685 * the absolute bounds could be calculated by:
686 *
687 * <pre class="prettyprint">
688 * int offset[] = new int[2];
689 * getLocationOnScreen(offset);
690 * Rect absBounds = new Rect(bounds.left + offset[0],
691 * bounds.top + offset[1],
692 * bounds.right + offset[0], bounds.bottom + offset[1]);
693 * </pre>
694 *
695 * @param view the virtual view parent.
696 * @param virtualId id identifying the virtual child inside the parent view.
697 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700698 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700699 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
700 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700701 }
702
Felipe Leme2ac463e2017-03-13 14:06:25 -0700703 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700704 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800705 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700706 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800707 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700708 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700709 notifyViewEntered(view, 0);
710 }
711
Felipe Leme17292d12017-10-24 14:03:10 -0700712 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800713 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700714 if (sVerbose) {
715 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
716 + ") on state " + getStateAsStringLocked());
717 }
718 return true;
719 }
Felipe Leme3103c632017-12-18 15:05:14 -0800720 if (sVerbose && isFinishedLocked()) {
721 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
722 + ") on state " + getStateAsStringLocked());
Felipe Leme17292d12017-10-24 14:03:10 -0700723 }
724 return false;
725 }
726
Dake Gu67decfa2017-12-27 11:48:08 -0800727 private boolean isClientVisibleForAutofillLocked() {
728 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800729 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800730 }
731
732 private boolean isClientDisablingEnterExitEvent() {
733 final AutofillClient client = getClient();
734 return client != null && client.isDisablingEnterExitEventForAutofill();
735 }
736
Felipe Lemed1146422017-04-26 10:17:05 -0700737 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700738 if (!hasAutofillFeature()) {
739 return;
740 }
Dake Gu67decfa2017-12-27 11:48:08 -0800741 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700742 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800743 callback = notifyViewEnteredLocked(view, flags);
744 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700745
Dake Gu67decfa2017-12-27 11:48:08 -0800746 if (callback != null) {
747 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
748 }
749 }
Svet Ganov782043c2017-02-11 00:52:02 +0000750
Dake Gu67decfa2017-12-27 11:48:08 -0800751 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
752 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
753 if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
754
755 AutofillCallback callback = null;
756
757 ensureServiceClientAddedIfNeededLocked();
758
759 if (!mEnabled) {
760 if (mCallback != null) {
761 callback = mCallback;
762 }
763 } else {
764 // don't notify entered when Activity is already in background
765 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700766 final AutofillId id = getAutofillId(view);
767 final AutofillValue value = view.getAutofillValue();
768
Felipe Lemec24a56a2017-08-03 14:27:57 -0700769 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700770 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700771 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700772 } else {
773 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700774 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700775 }
Felipe Leme24aae152017-03-15 12:33:01 -0700776 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800777 }
Dake Gu67decfa2017-12-27 11:48:08 -0800778 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800779 }
780
781 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700782 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800783 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700784 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800785 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700786 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700787 if (!hasAutofillFeature()) {
788 return;
789 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700790 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800791 notifyViewExitedLocked(view);
792 }
793 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800794
Dake Gu67decfa2017-12-27 11:48:08 -0800795 void notifyViewExitedLocked(@NonNull View view) {
796 ensureServiceClientAddedIfNeededLocked();
797
798 if (mEnabled && isActiveLocked()) {
799 // dont notify exited when Activity is already in background
800 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700801 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800802
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700803 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700804 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700805 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800806 }
807 }
808
809 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700810 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700811 *
812 * @param view {@link View} that was exited.
813 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700814 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700815 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
816 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
817 }
818
819 /**
820 * Called when a virtual view's visibility changed.
821 *
822 * @param view {@link View} that was exited.
823 * @param virtualId id identifying the virtual child inside the parent view.
824 * @param isVisible visible if the view is visible in the view hierarchy.
825 */
826 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
827 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
828 }
829
830 /**
831 * Called when a view/virtual view's visibility changed.
832 *
833 * @param view {@link View} that was exited.
834 * @param virtualId id identifying the virtual child inside the parent view.
835 * @param isVisible visible if the view is visible in the view hierarchy.
836 * @param virtual Whether the view is virtual.
837 */
838 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
839 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700840 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700841 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700842 final AutofillId id = virtual ? getAutofillId(view, virtualId)
843 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700844 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700845 if (mFillableIds.contains(id)) {
846 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
847 requestHideFillUi(id, view);
848 }
849 }
850 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800851 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700852 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700853 }
854 }
855 }
856
857 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700858 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800859 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700860 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
861 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
862 * the absolute bounds could be calculated by:
863 *
864 * <pre class="prettyprint">
865 * int offset[] = new int[2];
866 * getLocationOnScreen(offset);
867 * Rect absBounds = new Rect(bounds.left + offset[0],
868 * bounds.top + offset[1],
869 * bounds.right + offset[0], bounds.bottom + offset[1]);
870 * </pre>
871 *
872 * @param view the virtual view parent.
873 * @param virtualId id identifying the virtual child inside the parent view.
874 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800875 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700876 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
877 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700878 }
879
Felipe Leme6dcec872017-05-25 11:24:23 -0700880 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700881 if (!hasAutofillFeature()) {
882 return;
883 }
Dake Gu67decfa2017-12-27 11:48:08 -0800884 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700885 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800886 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
887 }
Felipe Leme17292d12017-10-24 14:03:10 -0700888
Dake Gu67decfa2017-12-27 11:48:08 -0800889 if (callback != null) {
890 callback.onAutofillEvent(view, virtualId,
891 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
892 }
893 }
Svet Ganov782043c2017-02-11 00:52:02 +0000894
Dake Gu67decfa2017-12-27 11:48:08 -0800895 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
896 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
897 int flags) {
898 AutofillCallback callback = null;
899 if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
900
901 ensureServiceClientAddedIfNeededLocked();
902
903 if (!mEnabled) {
904 if (mCallback != null) {
905 callback = mCallback;
906 }
907 } else {
908 // don't notify entered when Activity is already in background
909 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700910 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700911
Felipe Lemec24a56a2017-08-03 14:27:57 -0700912 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700913 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700914 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700915 } else {
916 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700917 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700918 }
Felipe Leme24aae152017-03-15 12:33:01 -0700919 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800920 }
Dake Gu67decfa2017-12-27 11:48:08 -0800921 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800922 }
923
924 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700925 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800926 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700927 * @param view the virtual view parent.
928 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800929 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700930 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700931 if (!hasAutofillFeature()) {
932 return;
933 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700934 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800935 notifyViewExitedLocked(view, virtualId);
936 }
937 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800938
Dake Gu67decfa2017-12-27 11:48:08 -0800939 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
940 ensureServiceClientAddedIfNeededLocked();
941
942 if (mEnabled && isActiveLocked()) {
943 // don't notify exited when Activity is already in background
944 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700945 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800946
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700947 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700948 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700949 }
Svet Ganov782043c2017-02-11 00:52:02 +0000950 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800951 }
952
953 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800954 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800955 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800956 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800957 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700958 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700959 if (!hasAutofillFeature()) {
960 return;
961 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700962 AutofillId id = null;
963 boolean valueWasRead = false;
964 AutofillValue value = null;
965
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700966 synchronized (mLock) {
967 // If the session is gone some fields might still be highlighted, hence we have to
968 // remove the isAutofilled property even if no sessions are active.
969 if (mLastAutofilledData == null) {
970 view.setAutofilled(false);
971 } else {
972 id = getAutofillId(view);
973 if (mLastAutofilledData.containsKey(id)) {
974 value = view.getAutofillValue();
975 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700976
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700977 if (Objects.equals(mLastAutofilledData.get(id), value)) {
978 view.setAutofilled(true);
979 } else {
980 view.setAutofilled(false);
981 mLastAutofilledData.remove(id);
982 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700983 } else {
984 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700985 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700986 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700987
Felipe Lemec24a56a2017-08-03 14:27:57 -0700988 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700989 if (sVerbose && mEnabled) {
990 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
991 + getStateAsStringLocked());
992 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700993 return;
994 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800995
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700996 if (id == null) {
997 id = getAutofillId(view);
998 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700999
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001000 if (!valueWasRead) {
1001 value = view.getAutofillValue();
1002 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001003
Felipe Leme0aa4c502017-04-26 12:36:01 -07001004 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001005 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001006 }
1007
Felipe Lemebab851c2017-02-03 18:45:08 -08001008 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001009 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001010 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001011 * @param view the virtual view parent.
1012 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001013 * @param value new value of the child.
1014 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001015 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001016 if (!hasAutofillFeature()) {
1017 return;
1018 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001019 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001020 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001021 return;
1022 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001023
Felipe Leme6dcec872017-05-25 11:24:23 -07001024 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001025 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001026 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001027 }
1028
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001029
1030 /**
1031 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
1032 *
1033 * @hide
1034 */
1035 public void notifyViewClicked(View view) {
1036 final AutofillId id = view.getAutofillId();
1037
1038 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1039
1040 synchronized (mLock) {
1041 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1042 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1043 commitLocked();
1044 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
1045 mContext.getPackageName());
1046 }
1047 }
1048 }
1049
1050 /**
1051 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1052 *
1053 * @hide
1054 */
1055 public void onActivityFinished() {
1056 if (!hasAutofillFeature()) {
1057 return;
1058 }
1059 synchronized (mLock) {
1060 if (mSaveOnFinish) {
Felipe Leme601d2202017-11-17 08:21:23 -08001061 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001062 commitLocked();
1063 } else {
1064 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
1065 cancelLocked();
1066 }
1067 }
1068 }
1069
Felipe Lemebab851c2017-02-03 18:45:08 -08001070 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001071 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001072 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001073 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1074 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1075 * that represent the HTML elements, it should call this method after the form is submitted and
1076 * another page is rendered.
1077 *
1078 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1079 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001080 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001081 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001082 if (!hasAutofillFeature()) {
1083 return;
1084 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001085 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001086 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001087 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001088 }
1089
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001090 private void commitLocked() {
1091 if (!mEnabled && !isActiveLocked()) {
1092 return;
1093 }
1094 finishSessionLocked();
1095 }
1096
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001097 /**
1098 * Called to indicate the current autofill context should be cancelled.
1099 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001100 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1101 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1102 * that represent the HTML elements, it should call this method if the user does not post the
1103 * form but moves to another form in this page.
1104 *
1105 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1106 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001107 */
1108 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001109 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001110 if (!hasAutofillFeature()) {
1111 return;
1112 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001113 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001114 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001115 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001116 }
1117
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001118 private void cancelLocked() {
1119 if (!mEnabled && !isActiveLocked()) {
1120 return;
1121 }
1122 cancelSessionLocked();
1123 }
1124
Svet Ganovf965b872017-04-28 16:34:02 -07001125 /** @hide */
1126 public void disableOwnedAutofillServices() {
1127 disableAutofillServices();
1128 }
1129
Svet Ganovf20a0372017-04-10 17:08:05 -07001130 /**
1131 * If the app calling this API has enabled autofill services they
1132 * will be disabled.
1133 */
Svet Ganovf965b872017-04-28 16:34:02 -07001134 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001135 if (!hasAutofillFeature()) {
1136 return;
1137 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001138 try {
1139 mService.disableOwnedAutofillServices(mContext.getUserId());
1140 } catch (RemoteException e) {
1141 throw e.rethrowFromSystemServer();
1142 }
1143 }
1144
Felipe Lemedb041182017-04-21 17:33:38 -07001145 /**
1146 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1147 * enabled for the current user, or {@code false} otherwise.
1148 */
1149 public boolean hasEnabledAutofillServices() {
1150 if (mService == null) return false;
1151
1152 try {
1153 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1154 } catch (RemoteException e) {
1155 throw e.rethrowFromSystemServer();
1156 }
1157 }
1158
1159 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001160 * Returns the component name of the {@link AutofillService} that is enabled for the current
1161 * user.
1162 */
1163 @Nullable
1164 public ComponentName getAutofillServiceComponentName() {
1165 if (mService == null) return null;
1166
1167 try {
1168 return mService.getAutofillServiceComponentName();
1169 } catch (RemoteException e) {
1170 throw e.rethrowFromSystemServer();
1171 }
1172 }
1173
1174 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001175 * Gets the id of the {@link UserData} used for
1176 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1177 *
1178 * <p>This method is useful when the service must check the status of the {@link UserData} in
1179 * the device without fetching the whole object.
1180 *
1181 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1182 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1183 * the user.
1184 *
1185 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1186 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1187 * service for the user.
1188 */
1189 @Nullable public String getUserDataId() {
1190 try {
1191 return mService.getUserDataId();
1192 } catch (RemoteException e) {
1193 e.rethrowFromSystemServer();
1194 return null;
1195 }
1196 }
1197
1198 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001199 * Gets the user data used for
1200 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001201 *
Felipe Leme27f45732017-12-22 09:05:22 -08001202 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1203 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1204 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001205 *
Felipe Leme452886a2017-11-27 13:09:13 -08001206 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1207 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001208 */
Felipe Leme452886a2017-11-27 13:09:13 -08001209 @Nullable public UserData getUserData() {
1210 try {
1211 return mService.getUserData();
1212 } catch (RemoteException e) {
1213 e.rethrowFromSystemServer();
1214 return null;
1215 }
1216 }
1217
1218 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001219 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001220 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001221 *
1222 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1223 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1224 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001225 */
Felipe Leme452886a2017-11-27 13:09:13 -08001226 public void setUserData(@Nullable UserData userData) {
1227 try {
1228 mService.setUserData(userData);
1229 } catch (RemoteException e) {
1230 e.rethrowFromSystemServer();
1231 }
1232 }
1233
1234 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001235 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1236 * enabled.
1237 *
1238 * <p>As field classification is an expensive operation, it could be disabled, either
1239 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1240 * permanently (for example, because the device is a low-level device).
1241 *
1242 * <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 Leme329d0402017-12-06 09:22:43 -08001245 */
Felipe Leme329d0402017-12-06 09:22:43 -08001246 public boolean isFieldClassificationEnabled() {
1247 try {
1248 return mService.isFieldClassificationEnabled();
1249 } catch (RemoteException e) {
1250 e.rethrowFromSystemServer();
1251 return false;
1252 }
1253 }
1254
1255 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001256 * Gets the name of the default algorithm used for
1257 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1258 *
1259 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1260 * set.
1261 *
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.
1265 */
1266 @Nullable
1267 public String getDefaultFieldClassificationAlgorithm() {
1268 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001269 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001270 } catch (RemoteException e) {
1271 e.rethrowFromSystemServer();
1272 return null;
1273 }
1274 }
1275
1276 /**
1277 * Gets the name of all algorithms currently available for
1278 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1279 *
1280 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001281 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1282 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001283 */
1284 @NonNull
1285 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001286 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001287 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001288 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1289 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001290 } catch (RemoteException e) {
1291 e.rethrowFromSystemServer();
1292 return null;
1293 }
1294 }
1295
1296 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001297 * Returns {@code true} if autofill is supported by the current device and
1298 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001299 *
1300 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1301 * <ol>
1302 * <li>Low-end devices.
1303 * <li>Device policy rules that forbid its usage.
1304 * </ol>
1305 */
1306 public boolean isAutofillSupported() {
1307 if (mService == null) return false;
1308
1309 try {
1310 return mService.isServiceSupported(mContext.getUserId());
1311 } catch (RemoteException e) {
1312 throw e.rethrowFromSystemServer();
1313 }
1314 }
1315
Felipe Leme637e05e2017-12-06 12:09:37 -08001316 // Note: don't need to use locked suffix because mContext is final.
1317 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001318 final AutofillClient client = mContext.getAutofillClient();
1319 if (client == null && sDebug) {
1320 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1321 + mContext);
1322 }
1323 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001324 }
1325
1326 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001327 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001328 if (!hasAutofillFeature()) {
1329 return;
1330 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001331 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001332 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1333 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1334 // service set the extra and returned RESULT_CANCELED...
1335
Felipe Leme9f9ee252017-04-27 13:56:22 -07001336 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001337
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001338 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001339 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001340 return;
1341 }
Dake Gu67decfa2017-12-27 11:48:08 -08001342 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1343 // of app activity. We enforce enter event to re-show fill ui in such case.
1344 // CTS example:
1345 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1346 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1347 if (!mOnInvisibleCalled && focusView != null
1348 && focusView.canNotifyAutofillEnterExitEvent()) {
1349 notifyViewExitedLocked(focusView);
1350 notifyViewEnteredLocked(focusView, 0);
1351 }
1352 if (data == null) {
1353 // data is set to null when result is not RESULT_OK
1354 return;
1355 }
1356
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001357 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1358 final Bundle responseData = new Bundle();
1359 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001360 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1361 if (newClientState != null) {
1362 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1363 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001364 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001365 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1366 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001367 } catch (RemoteException e) {
1368 Log.e(TAG, "Error delivering authentication result", e);
1369 }
Svet Ganov782043c2017-02-11 00:52:02 +00001370 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001371 }
1372
Felipe Leme640f30a2017-03-06 15:44:06 -08001373 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001374 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001375 }
1376
Felipe Leme6dcec872017-05-25 11:24:23 -07001377 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001378 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001379 }
1380
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001381 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1382 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001383 if (sVerbose) {
1384 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001385 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
1386 + ", compatMode=" + isCompatibilityModeEnabledLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001387 }
Felipe Leme3103c632017-12-18 15:05:14 -08001388 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001389 if (sVerbose) {
1390 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001391 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001392 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001393 return;
1394 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001395 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001396 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001397 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001398
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001399 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001400 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001401 mCallback != null, flags, client.autofillClientGetComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001402 if (mSessionId != NO_SESSION) {
1403 mState = STATE_ACTIVE;
1404 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001405 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001406 } catch (RemoteException e) {
1407 throw e.rethrowFromSystemServer();
1408 }
1409 }
1410
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001411 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001412 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001413
1414 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001415
Svet Ganov782043c2017-02-11 00:52:02 +00001416 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001417 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001418 } catch (RemoteException e) {
1419 throw e.rethrowFromSystemServer();
1420 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001421
Felipe Lemec24a56a2017-08-03 14:27:57 -07001422 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001423 }
1424
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001425 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001426 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001427
1428 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001429
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001430 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001431 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001432 } catch (RemoteException e) {
1433 throw e.rethrowFromSystemServer();
1434 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001435
Svet Ganov48f10a22017-04-26 18:49:30 -07001436 resetSessionLocked();
1437 }
1438
1439 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001440 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001441 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001442 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001443 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001444 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001445 }
1446
Felipe Leme0aa4c502017-04-26 12:36:01 -07001447 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1448 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001449 if (sVerbose && action != ACTION_VIEW_EXITED) {
1450 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1451 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001452 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001453 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1454
Felipe Leme3461d3c2017-01-19 08:54:55 -08001455 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001456 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001457 final AutofillClient client = getClient();
1458 if (client == null) return; // NOTE: getClient() already logd it..
1459
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001460 final int newId = mService.updateOrRestartSession(
1461 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001462 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001463 mCallback != null, flags, client.autofillClientGetComponentName(),
1464 mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001465 if (newId != mSessionId) {
1466 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1467 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001468 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001469 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001470 }
1471 } else {
1472 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1473 mContext.getUserId());
1474 }
1475
Felipe Leme3461d3c2017-01-19 08:54:55 -08001476 } catch (RemoteException e) {
1477 throw e.rethrowFromSystemServer();
1478 }
1479 }
Svet Ganov782043c2017-02-11 00:52:02 +00001480
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001481 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001482 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001483 return;
1484 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001485
Svet Ganov782043c2017-02-11 00:52:02 +00001486 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001487 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001488 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001489 final int userId = mContext.getUserId();
1490 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001491 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1492 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1493 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001494 final IAutoFillManager service = mService;
1495 final IAutoFillManagerClient serviceClient = mServiceClient;
1496 mServiceClientCleaner = Cleaner.create(this, () -> {
1497 try {
1498 service.removeClient(serviceClient, userId);
1499 } catch (RemoteException e) {
1500 }
1501 });
Svet Ganov782043c2017-02-11 00:52:02 +00001502 } catch (RemoteException e) {
1503 throw e.rethrowFromSystemServer();
1504 }
1505 }
1506 }
1507
Felipe Lemee6010f22017-03-03 11:19:51 -08001508 /**
1509 * Registers a {@link AutofillCallback} to receive autofill events.
1510 *
1511 * @param callback callback to receive events.
1512 */
1513 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001514 if (!hasAutofillFeature()) {
1515 return;
1516 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001517 synchronized (mLock) {
1518 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001519
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001520 final boolean hadCallback = mCallback != null;
1521 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001522
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001523 if (!hadCallback) {
1524 try {
1525 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1526 } catch (RemoteException e) {
1527 throw e.rethrowFromSystemServer();
1528 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001529 }
1530 }
1531 }
1532
1533 /**
1534 * Unregisters a {@link AutofillCallback} to receive autofill events.
1535 *
1536 * @param callback callback to stop receiving events.
1537 */
1538 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001539 if (!hasAutofillFeature()) {
1540 return;
1541 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001542 synchronized (mLock) {
1543 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001544
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001545 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001546
Felipe Lemee6010f22017-03-03 11:19:51 -08001547 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001548 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001549 } catch (RemoteException e) {
1550 throw e.rethrowFromSystemServer();
1551 }
1552 }
1553 }
1554
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001555 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1556 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1557 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001558 if (anchor == null) {
1559 return;
1560 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001561
1562 AutofillCallback callback = null;
1563 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001564 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001565 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001566
1567 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001568 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001569 anchorBounds, presenter) && mCallback != null) {
1570 callback = mCallback;
1571 }
1572 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001573 }
1574 }
1575
1576 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001577 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001578 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001579 AutofillCallback.EVENT_INPUT_SHOWN);
1580 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001581 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001582 }
1583 }
1584 }
1585
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001586 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1587 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001588 synchronized (mLock) {
1589 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001590 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001591 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001592 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1593 // before onAuthenticationResult()
1594 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001595 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001596 }
1597 }
1598 }
1599 }
1600
Felipe Leme51e29da2017-10-24 14:03:10 -07001601 /** @hide */
1602 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1603 /** @hide */
1604 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1605 /** @hide */
1606 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1607 /** @hide */
1608 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1609 /** @hide */
1610 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1611
1612 private void setState(int flags) {
1613 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001614 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001615 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1616 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001617 // Reset the session state
1618 resetSessionLocked();
1619 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001620 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001621 // Reset connection to system
1622 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001623 if (mServiceClientCleaner != null) {
1624 mServiceClientCleaner.clean();
1625 mServiceClientCleaner = null;
1626 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001627 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001628 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001629 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1630 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001631 }
1632
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001633 /**
1634 * Sets a view as autofilled if the current value is the {code targetValue}.
1635 *
1636 * @param view The view that is to be autofilled
1637 * @param targetValue The value we want to fill into view
1638 */
1639 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1640 AutofillValue currentValue = view.getAutofillValue();
1641 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001642 synchronized (mLock) {
1643 if (mLastAutofilledData == null) {
1644 mLastAutofilledData = new ParcelableMap(1);
1645 }
1646 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001647 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001648 view.setAutofilled(true);
1649 }
1650 }
1651
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001652 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001653 synchronized (mLock) {
1654 if (sessionId != mSessionId) {
1655 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001656 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001657
Felipe Leme637e05e2017-12-06 12:09:37 -08001658 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001659 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001660 return;
1661 }
1662
1663 final int itemCount = ids.size();
1664 int numApplied = 0;
1665 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001666 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1667 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001668
1669 for (int i = 0; i < itemCount; i++) {
1670 final AutofillId id = ids.get(i);
1671 final AutofillValue value = values.get(i);
1672 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001673 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001674 if (view == null) {
1675 Log.w(TAG, "autofill(): no View with id " + viewId);
1676 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001677 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001678 if (id.isVirtual()) {
1679 if (virtualValues == null) {
1680 // Most likely there will be just one view with virtual children.
1681 virtualValues = new ArrayMap<>(1);
1682 }
1683 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1684 if (valuesByParent == null) {
1685 // We don't know the size yet, but usually it will be just a few fields...
1686 valuesByParent = new SparseArray<>(5);
1687 virtualValues.put(view, valuesByParent);
1688 }
1689 valuesByParent.put(id.getVirtualChildId(), value);
1690 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001691 // Mark the view as to be autofilled with 'value'
1692 if (mLastAutofilledData == null) {
1693 mLastAutofilledData = new ParcelableMap(itemCount - i);
1694 }
1695 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001696
1697 view.autofill(value);
1698
1699 // Set as autofilled if the values match now, e.g. when the value was updated
1700 // synchronously.
1701 // If autofill happens async, the view is set to autofilled in
1702 // notifyValueChanged.
1703 setAutofilledIfValuesIs(view, value);
1704
1705 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001706 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001707 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001708
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001709 if (virtualValues != null) {
1710 for (int i = 0; i < virtualValues.size(); i++) {
1711 final View parent = virtualValues.keyAt(i);
1712 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1713 parent.autofill(childrenValues);
1714 numApplied += childrenValues.size();
1715 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001716 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001717
Felipe Lemeb22d6352017-09-08 20:03:53 -07001718 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1719 .setPackageName(mContext.getPackageName())
1720 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1721 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001722 mMetricsLogger.write(log);
1723 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001724 }
1725
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001726 /**
1727 * Set the tracked views.
1728 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001729 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001730 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001731 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001732 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001733 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001734 */
Felipe Leme27e20222017-05-18 15:24:11 -07001735 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001736 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1737 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001738 synchronized (mLock) {
1739 if (mEnabled && mSessionId == sessionId) {
1740 if (saveOnAllViewsInvisible) {
1741 mTrackedViews = new TrackedViews(trackedIds);
1742 } else {
1743 mTrackedViews = null;
1744 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001745 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001746 if (fillableIds != null) {
1747 if (mFillableIds == null) {
1748 mFillableIds = new ArraySet<>(fillableIds.length);
1749 }
1750 for (AutofillId id : fillableIds) {
1751 mFillableIds.add(id);
1752 }
1753 if (sVerbose) {
1754 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1755 + ", mFillableIds" + mFillableIds);
1756 }
1757 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001758
1759 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1760 // Turn off trigger on previous view id.
1761 setNotifyOnClickLocked(mSaveTriggerId, false);
1762 }
1763
1764 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1765 // Turn on trigger on new view id.
1766 mSaveTriggerId = saveTriggerId;
1767 setNotifyOnClickLocked(mSaveTriggerId, true);
1768 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001769 }
1770 }
1771 }
1772
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001773 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1774 final View view = findView(id);
1775 if (view == null) {
1776 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1777 return;
1778 }
1779 view.setNotifyAutofillManagerOnClick(notify);
1780 }
1781
Felipe Lemec24a56a2017-08-03 14:27:57 -07001782 private void setSaveUiState(int sessionId, boolean shown) {
1783 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1784 synchronized (mLock) {
1785 if (mSessionId != NO_SESSION) {
1786 // Race condition: app triggered a new session after the previous session was
1787 // finished but before server called setSaveUiState() - need to cancel the new
1788 // session to avoid further inconsistent behavior.
1789 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1790 + ") called on existing session " + mSessionId + "; cancelling it");
1791 cancelSessionLocked();
1792 }
1793 if (shown) {
1794 mSessionId = sessionId;
1795 mState = STATE_SHOWING_SAVE_UI;
1796 } else {
1797 mSessionId = NO_SESSION;
1798 mState = STATE_UNKNOWN;
1799 }
1800 }
1801 }
1802
Felipe Leme650f7ab2017-09-19 13:08:24 -07001803 /**
1804 * Marks the state of the session as finished.
1805 *
1806 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001807 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1808 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1809 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001810 */
1811 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001812 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001813 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001814 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001815 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001816 }
1817 }
1818
Felipe Leme27e20222017-05-18 15:24:11 -07001819 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001820 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001821 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001822 if (anchor == null) {
1823 return;
1824 }
Felipe Leme27e20222017-05-18 15:24:11 -07001825 requestHideFillUi(id, anchor);
1826 }
1827
1828 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001829
1830 AutofillCallback callback = null;
1831 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001832 // We do not check the session id for two reasons:
1833 // 1. If local and remote session id are off sync the UI would be stuck shown
1834 // 2. There is a race between the user state being destroyed due the fill
1835 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001836 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001837 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001838 if (client.autofillClientRequestHideFillUi() && mCallback != null) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001839 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001840 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001841 }
1842 }
1843
1844 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001845 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001846 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001847 AutofillCallback.EVENT_INPUT_HIDDEN);
1848 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001849 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001850 }
1851 }
1852 }
1853
Felipe Leme17292d12017-10-24 14:03:10 -07001854 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001855 if (sVerbose) {
1856 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001857 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001858 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001859 final View anchor = findView(id);
1860 if (anchor == null) {
1861 return;
1862 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001863
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001864 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001865 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001866 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001867 callback = mCallback;
1868 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001869 }
1870
1871 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001872 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001873 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001874 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1875 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001876 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001877 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001878 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001879
Felipe Leme17292d12017-10-24 14:03:10 -07001880 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001881 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001882 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001883 }
1884 }
1885
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001886 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001887 * Find a single view by its id.
1888 *
1889 * @param autofillId The autofill id of the view
1890 *
1891 * @return The view or {@code null} if view was not found
1892 */
1893 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001894 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001895 if (client != null) {
1896 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08001897 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001898 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001899 }
1900
Felipe Lemebb810922017-04-25 15:54:06 -07001901 /** @hide */
1902 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001903 return mService != null;
1904 }
1905
Felipe Lemec24a56a2017-08-03 14:27:57 -07001906 /** @hide */
1907 public void onPendingSaveUi(int operation, IBinder token) {
1908 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1909
1910 synchronized (mLock) {
1911 try {
1912 mService.onPendingSaveUi(operation, token);
1913 } catch (RemoteException e) {
1914 e.rethrowFromSystemServer();
1915 }
1916 }
1917 }
1918
1919 /** @hide */
1920 public void dump(String outerPrefix, PrintWriter pw) {
1921 pw.print(outerPrefix); pw.println("AutofillManager:");
1922 final String pfx = outerPrefix + " ";
1923 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001924 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001925 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001926 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001927 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1928 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1929 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08001930 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001931 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1932 pw.print(pfx); pw.print("tracked views: ");
1933 if (mTrackedViews == null) {
1934 pw.println("null");
1935 } else {
1936 final String pfx2 = pfx + " ";
1937 pw.println();
1938 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1939 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1940 }
1941 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001942 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1943 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
felipealfd3f8f62018-02-07 11:49:07 +01001944 pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
1945 isCompatibilityModeEnabledLocked());
Felipe Leme51e29da2017-10-24 14:03:10 -07001946 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1947 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001948 }
1949
Felipe Lemec7b45292017-09-19 09:06:20 -07001950 private String getStateAsStringLocked() {
1951 switch (mState) {
1952 case STATE_UNKNOWN:
1953 return "STATE_UNKNOWN";
1954 case STATE_ACTIVE:
1955 return "STATE_ACTIVE";
1956 case STATE_FINISHED:
1957 return "STATE_FINISHED";
1958 case STATE_SHOWING_SAVE_UI:
1959 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001960 case STATE_DISABLED_BY_SERVICE:
1961 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001962 default:
1963 return "INVALID:" + mState;
1964 }
1965 }
1966
Felipe Lemec24a56a2017-08-03 14:27:57 -07001967 private boolean isActiveLocked() {
1968 return mState == STATE_ACTIVE;
1969 }
1970
Felipe Leme3103c632017-12-18 15:05:14 -08001971 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07001972 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001973 }
1974
Felipe Leme3103c632017-12-18 15:05:14 -08001975 private boolean isFinishedLocked() {
1976 return mState == STATE_FINISHED;
1977 }
1978
Felipe Leme9876a6f2017-05-30 15:47:28 -07001979 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001980 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07001981 if (client == null) {
1982 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1983 return;
1984 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001985 client.autofillClientRunOnUiThread(runnable);
1986 }
1987
1988 /**
1989 * Implementation of the accessibility based compatibility.
1990 */
1991 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
1992 @GuardedBy("mLock")
1993 private final Rect mFocusedBounds = new Rect();
1994 @GuardedBy("mLock")
1995 private final Rect mTempBounds = new Rect();
1996
1997 @GuardedBy("mLock")
1998 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
1999 @GuardedBy("mLock")
2000 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2001
2002 // Need to report a fake service in case a11y clients check the service list
2003 @NonNull
2004 @GuardedBy("mLock")
2005 AccessibilityServiceInfo mCompatServiceInfo;
2006
2007 CompatibilityBridge() {
2008 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2009 am.setAccessibilityPolicy(this);
2010 }
2011
2012 private AccessibilityServiceInfo getCompatServiceInfo() {
2013 synchronized (mLock) {
2014 if (mCompatServiceInfo != null) {
2015 return mCompatServiceInfo;
2016 }
2017 final Intent intent = new Intent();
2018 intent.setComponent(new ComponentName("android",
2019 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2020 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2021 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2022 try {
2023 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2024 } catch (XmlPullParserException | IOException e) {
2025 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2026 throw new IllegalStateException("Cannot find compat autofill service");
2027 }
2028 return mCompatServiceInfo;
2029 }
2030 }
2031
2032 @Override
2033 public boolean isEnabled(boolean accessibilityEnabled) {
2034 return true;
2035 }
2036
2037 @Override
2038 public int getRelevantEventTypes(int relevantEventTypes) {
2039 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2040 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
2041 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2042 }
2043
2044 @Override
2045 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2046 List<AccessibilityServiceInfo> installedServices) {
2047 if (installedServices == null) {
2048 installedServices = new ArrayList<>();
2049 }
2050 installedServices.add(getCompatServiceInfo());
2051 return installedServices;
2052 }
2053
2054 @Override
2055 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2056 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2057 if (enabledService == null) {
2058 enabledService = new ArrayList<>();
2059 }
2060 enabledService.add(getCompatServiceInfo());
2061 return enabledService;
2062 }
2063
2064 @Override
2065 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2066 boolean accessibilityEnabled, int relevantEventTypes) {
2067 switch (event.getEventType()) {
2068 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2069 synchronized (mLock) {
2070 if (mFocusedWindowId == event.getWindowId()
2071 && mFocusedNodeId == event.getSourceNodeId()) {
2072 return event;
2073 }
2074 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2075 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2076 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2077 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2078 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2079 mFocusedBounds.set(0, 0, 0, 0);
2080 }
2081 final int windowId = event.getWindowId();
2082 final long nodeId = event.getSourceNodeId();
2083 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2084 mFocusedWindowId = windowId;
2085 mFocusedNodeId = nodeId;
2086 }
2087 }
2088 } break;
2089
2090 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2091 synchronized (mLock) {
2092 if (mFocusedWindowId == event.getWindowId()
2093 && mFocusedNodeId == event.getSourceNodeId()) {
2094 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2095 }
2096 }
2097 } break;
2098
2099 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2100 final AutofillClient client = getClient();
2101 if (client != null) {
2102 synchronized (mLock) {
2103 if (client.autofillClientIsFillUiShowing()) {
2104 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2105 }
2106 updateTrackedViewsLocked();
2107 }
2108 }
2109 } break;
2110 }
2111
2112 return accessibilityEnabled ? event : null;
2113 }
2114
2115 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2116 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2117 if (!isVirtualNode(virtualId)) {
2118 return false;
2119 }
2120 final View view = findViewByAccessibilityId(windowId, nodeId);
2121 if (view == null) {
2122 return false;
2123 }
2124 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2125 if (node == null) {
2126 return false;
2127 }
2128 if (!node.isEditable()) {
2129 return false;
2130 }
2131 final Rect newBounds = mTempBounds;
2132 node.getBoundsInScreen(newBounds);
2133 if (newBounds.equals(focusedBounds)) {
2134 return false;
2135 }
2136 focusedBounds.set(newBounds);
2137 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2138 return true;
2139 }
2140
2141 private void notifyViewExited(int windowId, long nodeId) {
2142 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2143 if (!isVirtualNode(virtualId)) {
2144 return;
2145 }
2146 final View view = findViewByAccessibilityId(windowId, nodeId);
2147 if (view == null) {
2148 return;
2149 }
2150 AutofillManager.this.notifyViewExited(view, virtualId);
2151 }
2152
2153 private void notifyValueChanged(int windowId, long nodeId) {
2154 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2155 if (!isVirtualNode(virtualId)) {
2156 return;
2157 }
2158 final View view = findViewByAccessibilityId(windowId, nodeId);
2159 if (view == null) {
2160 return;
2161 }
2162 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2163 if (node == null) {
2164 return;
2165 }
2166 AutofillManager.this.notifyValueChanged(view, virtualId,
2167 AutofillValue.forText(node.getText()));
2168 }
2169
2170 private void updateTrackedViewsLocked() {
2171 if (mTrackedViews != null) {
2172 mTrackedViews.onVisibleForAutofillChangedLocked();
2173 }
2174 }
2175
2176 private View findViewByAccessibilityId(int windowId, long nodeId) {
2177 final AutofillClient client = getClient();
2178 if (client == null) {
2179 return null;
2180 }
2181 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2182 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2183 }
2184
2185 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2186 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2187 if (provider == null) {
2188 return null;
2189 }
2190 return provider.createAccessibilityNodeInfo(virtualId);
2191 }
2192
2193 private boolean isVirtualNode(int nodeId) {
2194 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2195 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2196 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002197 }
2198
Felipe Lemee6010f22017-03-03 11:19:51 -08002199 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002200 * View tracking information. Once all tracked views become invisible the session is finished.
2201 */
2202 private class TrackedViews {
2203 /** Visible tracked views */
2204 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2205
2206 /** Invisible tracked views */
2207 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2208
2209 /**
2210 * Check if set is null or value is in set.
2211 *
2212 * @param set The set or null (== empty set)
2213 * @param value The value that might be in the set
2214 *
2215 * @return {@code true} iff set is not empty and value is in set
2216 */
Felipe Leme27e20222017-05-18 15:24:11 -07002217 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002218 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2219 return set != null && set.contains(value);
2220 }
2221
2222 /**
2223 * Add a value to a set. If set is null, create a new set.
2224 *
2225 * @param set The set or null (== empty set)
2226 * @param valueToAdd The value to add
2227 *
2228 * @return The set including the new value. If set was {@code null}, a set containing only
2229 * the new value.
2230 */
Felipe Leme27e20222017-05-18 15:24:11 -07002231 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002232 @NonNull
2233 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2234 if (set == null) {
2235 set = new ArraySet<>(1);
2236 }
2237
2238 set.add(valueToAdd);
2239
2240 return set;
2241 }
2242
2243 /**
2244 * Remove a value from a set.
2245 *
2246 * @param set The set or null (== empty set)
2247 * @param valueToRemove The value to remove
2248 *
2249 * @return The set without the removed value. {@code null} if set was null, or is empty
2250 * after removal.
2251 */
Felipe Leme27e20222017-05-18 15:24:11 -07002252 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002253 @Nullable
2254 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2255 if (set == null) {
2256 return null;
2257 }
2258
2259 set.remove(valueToRemove);
2260
2261 if (set.isEmpty()) {
2262 return null;
2263 }
2264
2265 return set;
2266 }
2267
2268 /**
2269 * Set the tracked views.
2270 *
2271 * @param trackedIds The views to be tracked
2272 */
Felipe Leme27e20222017-05-18 15:24:11 -07002273 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002274 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002275 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002276 final boolean[] isVisible;
2277
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002278 if (client.autofillClientIsVisibleForAutofill()) {
2279 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002280 } else {
2281 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002282 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002283 }
2284
Felipe Leme27e20222017-05-18 15:24:11 -07002285 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002286 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002287 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002288
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002289 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002290 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002291 } else {
2292 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2293 }
2294 }
2295 }
2296
Felipe Leme9f9ee252017-04-27 13:56:22 -07002297 if (sVerbose) {
2298 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002299 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2300 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2301 }
2302
2303 if (mVisibleTrackedIds == null) {
2304 finishSessionLocked();
2305 }
2306 }
2307
2308 /**
2309 * Called when a {@link View view's} visibility changes.
2310 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002311 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002312 * @param isVisible visible if the view is visible in the view hierarchy.
2313 */
Dake Gu67decfa2017-12-27 11:48:08 -08002314 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002315 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002316 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002317 + isVisible);
2318 }
2319
Dake Gu67decfa2017-12-27 11:48:08 -08002320 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002321 if (isVisible) {
2322 if (isInSet(mInvisibleTrackedIds, id)) {
2323 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2324 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2325 }
2326 } else {
2327 if (isInSet(mVisibleTrackedIds, id)) {
2328 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2329 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2330 }
2331 }
2332 }
2333
2334 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002335 if (sVerbose) {
2336 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2337 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002338 finishSessionLocked();
2339 }
2340 }
2341
2342 /**
2343 * Called once the client becomes visible.
2344 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002345 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002346 */
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002347 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002348 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002349 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002350 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002351 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2352 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2353 if (client != null) {
2354 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002355 final ArrayList<AutofillId> orderedInvisibleIds =
2356 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002357 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2358 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002359
2360 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2361 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2362 final AutofillId id = orderedInvisibleIds.get(i);
2363 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002364 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2365
Felipe Leme9f9ee252017-04-27 13:56:22 -07002366 if (sDebug) {
2367 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002368 }
2369 } else {
2370 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2371 }
2372 }
2373 }
2374
2375 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002376 final ArrayList<AutofillId> orderedVisibleIds =
2377 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002378 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2379 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002380
2381 final int numVisibleTrackedIds = orderedVisibleIds.size();
2382 for (int i = 0; i < numVisibleTrackedIds; i++) {
2383 final AutofillId id = orderedVisibleIds.get(i);
2384
2385 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002386 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2387 } else {
2388 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2389
Felipe Leme9f9ee252017-04-27 13:56:22 -07002390 if (sDebug) {
2391 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002392 }
2393 }
2394 }
2395 }
2396
2397 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2398 mVisibleTrackedIds = updatedVisibleTrackedIds;
2399 }
2400
2401 if (mVisibleTrackedIds == null) {
2402 finishSessionLocked();
2403 }
2404 }
2405 }
2406
2407 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002408 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002409 *
2410 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002411 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002412 */
2413 public abstract static class AutofillCallback {
2414
2415 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002416 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2417 EVENT_INPUT_SHOWN,
2418 EVENT_INPUT_HIDDEN,
2419 EVENT_INPUT_UNAVAILABLE
2420 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002421 @Retention(RetentionPolicy.SOURCE)
2422 public @interface AutofillEventType {}
2423
2424 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002425 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002426 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002427 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002428 * should be hidden upon receiving this event.
2429 */
2430 public static final int EVENT_INPUT_SHOWN = 1;
2431
2432 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002433 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002434 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002435 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002436 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2437 */
2438 public static final int EVENT_INPUT_HIDDEN = 2;
2439
2440 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002441 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002442 * autofill is not available.
2443 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002444 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002445 * to avoid flickering, it could shown it upon receiving this event.
2446 */
2447 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2448
2449 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002450 * Called after a change in the autofill state associated with a view.
2451 *
2452 * @param view view associated with the change.
2453 *
2454 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2455 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002456 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2457 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002458
2459 /**
2460 * Called after a change in the autofill state associated with a virtual view.
2461 *
2462 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002463 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002464 *
2465 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2466 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002467 public void onAutofillEvent(@NonNull View view, int virtualId,
2468 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002469 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002470 }
2471
Felipe Leme640f30a2017-03-06 15:44:06 -08002472 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2473 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002474
Felipe Leme640f30a2017-03-06 15:44:06 -08002475 AutofillManagerClient(AutofillManager autofillManager) {
2476 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002477 }
2478
2479 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002480 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002481 final AutofillManager afm = mAfm.get();
2482 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002483 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002484 }
2485 }
2486
2487 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002488 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002489 final AutofillManager afm = mAfm.get();
2490 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002491 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002492 }
2493 }
2494
2495 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002496 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2497 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002498 final AutofillManager afm = mAfm.get();
2499 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002500 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002501 }
2502 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002503
2504 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002505 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2506 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002507 final AutofillManager afm = mAfm.get();
2508 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002509 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2510 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002511 }
2512 }
2513
2514 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002515 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002516 final AutofillManager afm = mAfm.get();
2517 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002518 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002519 }
2520 }
2521
2522 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002523 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002524 final AutofillManager afm = mAfm.get();
2525 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002526 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002527 }
2528 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002529
2530 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002531 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002532 final AutofillManager afm = mAfm.get();
2533 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002534 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002535 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002536 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002537 } catch (IntentSender.SendIntentException e) {
2538 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2539 }
2540 });
2541 }
2542 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002543
2544 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002545 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002546 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2547 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002548 final AutofillManager afm = mAfm.get();
2549 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002550 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2551 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002552 }
2553 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002554
2555 @Override
2556 public void setSaveUiState(int sessionId, boolean shown) {
2557 final AutofillManager afm = mAfm.get();
2558 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002559 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2560 }
2561 }
2562
2563 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002564 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002565 final AutofillManager afm = mAfm.get();
2566 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002567 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002568 }
2569 }
Svet Ganov782043c2017-02-11 00:52:02 +00002570 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002571}