blob: 8b64bad8fe62620e63bfb8442c27c369190febf0 [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 */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800622 @GuardedBy("mLock")
felipealfd3f8f62018-02-07 11:49:07 +0100623 public boolean isCompatibilityModeEnabledLocked() {
624 return mCompatibilityBridge != null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800625 }
626
627 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700628 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700629 *
630 * <p>Typically used to determine whether the option to explicitly request autofill should
631 * be offered - see {@link #requestAutofill(View)}.
632 *
633 * @return whether autofill is enabled for the current user.
634 */
635 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800636 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700637 return false;
638 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700639 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800640 if (isDisabledByServiceLocked()) {
641 return false;
642 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700643 ensureServiceClientAddedIfNeededLocked();
644 return mEnabled;
645 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700646 }
647
648 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700649 * Should always be called from {@link AutofillService#getFillEventHistory()}.
650 *
651 * @hide
652 */
653 @Nullable public FillEventHistory getFillEventHistory() {
654 try {
655 return mService.getFillEventHistory();
656 } catch (RemoteException e) {
657 e.rethrowFromSystemServer();
658 return null;
659 }
660 }
661
662 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700663 * Explicitly requests a new autofill context.
664 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700665 * <p>Normally, the autofill context is automatically started if necessary when
666 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
667 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
668 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700669 *
670 * @param view view requesting the new autofill context.
671 */
672 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700673 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700674 }
675
676 /**
677 * Explicitly requests a new autofill context for virtual views.
678 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700679 * <p>Normally, the autofill context is automatically started if necessary when
680 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
681 * cases where it must be explicitly started. For example, when the virtual view offers an
682 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700683 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700684 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
685 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
686 * the absolute bounds could be calculated by:
687 *
688 * <pre class="prettyprint">
689 * int offset[] = new int[2];
690 * getLocationOnScreen(offset);
691 * Rect absBounds = new Rect(bounds.left + offset[0],
692 * bounds.top + offset[1],
693 * bounds.right + offset[0], bounds.bottom + offset[1]);
694 * </pre>
695 *
696 * @param view the virtual view parent.
697 * @param virtualId id identifying the virtual child inside the parent view.
698 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700699 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700700 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
701 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700702 }
703
Felipe Leme2ac463e2017-03-13 14:06:25 -0700704 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700705 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800706 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700707 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800708 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700709 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700710 notifyViewEntered(view, 0);
711 }
712
Andreas Gampe3f24e692018-02-05 13:24:28 -0800713 @GuardedBy("mLock")
Felipe Leme17292d12017-10-24 14:03:10 -0700714 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800715 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700716 if (sVerbose) {
717 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
718 + ") on state " + getStateAsStringLocked());
719 }
720 return true;
721 }
Felipe Leme3103c632017-12-18 15:05:14 -0800722 if (sVerbose && isFinishedLocked()) {
723 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
724 + ") on state " + getStateAsStringLocked());
Felipe Leme17292d12017-10-24 14:03:10 -0700725 }
726 return false;
727 }
728
Dake Gu67decfa2017-12-27 11:48:08 -0800729 private boolean isClientVisibleForAutofillLocked() {
730 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -0800731 return client != null && client.autofillClientIsVisibleForAutofill();
Dake Gu67decfa2017-12-27 11:48:08 -0800732 }
733
734 private boolean isClientDisablingEnterExitEvent() {
735 final AutofillClient client = getClient();
736 return client != null && client.isDisablingEnterExitEventForAutofill();
737 }
738
Felipe Lemed1146422017-04-26 10:17:05 -0700739 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700740 if (!hasAutofillFeature()) {
741 return;
742 }
Dake Gu67decfa2017-12-27 11:48:08 -0800743 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700744 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800745 callback = notifyViewEnteredLocked(view, flags);
746 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700747
Dake Gu67decfa2017-12-27 11:48:08 -0800748 if (callback != null) {
749 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
750 }
751 }
Svet Ganov782043c2017-02-11 00:52:02 +0000752
Dake Gu67decfa2017-12-27 11:48:08 -0800753 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800754 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800755 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
756 if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
757
758 AutofillCallback callback = null;
759
760 ensureServiceClientAddedIfNeededLocked();
761
762 if (!mEnabled) {
763 if (mCallback != null) {
764 callback = mCallback;
765 }
766 } else {
767 // don't notify entered when Activity is already in background
768 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700769 final AutofillId id = getAutofillId(view);
770 final AutofillValue value = view.getAutofillValue();
771
Felipe Lemec24a56a2017-08-03 14:27:57 -0700772 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700773 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700774 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700775 } else {
776 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700777 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700778 }
Felipe Leme24aae152017-03-15 12:33:01 -0700779 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800780 }
Dake Gu67decfa2017-12-27 11:48:08 -0800781 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800782 }
783
784 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700785 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800786 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700787 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800788 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700789 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700790 if (!hasAutofillFeature()) {
791 return;
792 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700793 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800794 notifyViewExitedLocked(view);
795 }
796 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800797
Andreas Gampe3f24e692018-02-05 13:24:28 -0800798 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800799 void notifyViewExitedLocked(@NonNull View view) {
800 ensureServiceClientAddedIfNeededLocked();
801
802 if (mEnabled && isActiveLocked()) {
803 // dont notify exited when Activity is already in background
804 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700805 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800806
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700807 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700808 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700809 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800810 }
811 }
812
813 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700814 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700815 *
816 * @param view {@link View} that was exited.
817 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700818 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700819 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
820 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
821 }
822
823 /**
824 * Called when a virtual view's visibility changed.
825 *
826 * @param view {@link View} that was exited.
827 * @param virtualId id identifying the virtual child inside the parent view.
828 * @param isVisible visible if the view is visible in the view hierarchy.
829 */
830 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
831 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
832 }
833
834 /**
835 * Called when a view/virtual view's visibility changed.
836 *
837 * @param view {@link View} that was exited.
838 * @param virtualId id identifying the virtual child inside the parent view.
839 * @param isVisible visible if the view is visible in the view hierarchy.
840 * @param virtual Whether the view is virtual.
841 */
842 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
843 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700844 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700845 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700846 final AutofillId id = virtual ? getAutofillId(view, virtualId)
847 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700848 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700849 if (mFillableIds.contains(id)) {
850 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
851 requestHideFillUi(id, view);
852 }
853 }
854 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800855 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700856 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700857 }
858 }
859 }
860
861 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700862 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800863 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700864 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
865 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
866 * the absolute bounds could be calculated by:
867 *
868 * <pre class="prettyprint">
869 * int offset[] = new int[2];
870 * getLocationOnScreen(offset);
871 * Rect absBounds = new Rect(bounds.left + offset[0],
872 * bounds.top + offset[1],
873 * bounds.right + offset[0], bounds.bottom + offset[1]);
874 * </pre>
875 *
876 * @param view the virtual view parent.
877 * @param virtualId id identifying the virtual child inside the parent view.
878 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800879 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700880 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
881 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700882 }
883
Felipe Leme6dcec872017-05-25 11:24:23 -0700884 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700885 if (!hasAutofillFeature()) {
886 return;
887 }
Dake Gu67decfa2017-12-27 11:48:08 -0800888 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700889 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800890 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
891 }
Felipe Leme17292d12017-10-24 14:03:10 -0700892
Dake Gu67decfa2017-12-27 11:48:08 -0800893 if (callback != null) {
894 callback.onAutofillEvent(view, virtualId,
895 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
896 }
897 }
Svet Ganov782043c2017-02-11 00:52:02 +0000898
Dake Gu67decfa2017-12-27 11:48:08 -0800899 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
Andreas Gampe3f24e692018-02-05 13:24:28 -0800900 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800901 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
902 int flags) {
903 AutofillCallback callback = null;
904 if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
905
906 ensureServiceClientAddedIfNeededLocked();
907
908 if (!mEnabled) {
909 if (mCallback != null) {
910 callback = mCallback;
911 }
912 } else {
913 // don't notify entered when Activity is already in background
914 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700915 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700916
Felipe Lemec24a56a2017-08-03 14:27:57 -0700917 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700918 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700919 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700920 } else {
921 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700922 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700923 }
Felipe Leme24aae152017-03-15 12:33:01 -0700924 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800925 }
Dake Gu67decfa2017-12-27 11:48:08 -0800926 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800927 }
928
929 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700930 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800931 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700932 * @param view the virtual view parent.
933 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800934 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700935 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700936 if (!hasAutofillFeature()) {
937 return;
938 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700939 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800940 notifyViewExitedLocked(view, virtualId);
941 }
942 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800943
Andreas Gampe3f24e692018-02-05 13:24:28 -0800944 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -0800945 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
946 ensureServiceClientAddedIfNeededLocked();
947
948 if (mEnabled && isActiveLocked()) {
949 // don't notify exited when Activity is already in background
950 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700951 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800952
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700953 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700954 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700955 }
Svet Ganov782043c2017-02-11 00:52:02 +0000956 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800957 }
958
959 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800960 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800961 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800962 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800963 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700964 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700965 if (!hasAutofillFeature()) {
966 return;
967 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700968 AutofillId id = null;
969 boolean valueWasRead = false;
970 AutofillValue value = null;
971
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700972 synchronized (mLock) {
973 // If the session is gone some fields might still be highlighted, hence we have to
974 // remove the isAutofilled property even if no sessions are active.
975 if (mLastAutofilledData == null) {
976 view.setAutofilled(false);
977 } else {
978 id = getAutofillId(view);
979 if (mLastAutofilledData.containsKey(id)) {
980 value = view.getAutofillValue();
981 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700982
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700983 if (Objects.equals(mLastAutofilledData.get(id), value)) {
984 view.setAutofilled(true);
985 } else {
986 view.setAutofilled(false);
987 mLastAutofilledData.remove(id);
988 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700989 } else {
990 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700991 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700992 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700993
Felipe Lemec24a56a2017-08-03 14:27:57 -0700994 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700995 if (sVerbose && mEnabled) {
996 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
997 + getStateAsStringLocked());
998 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700999 return;
1000 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001001
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001002 if (id == null) {
1003 id = getAutofillId(view);
1004 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001005
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001006 if (!valueWasRead) {
1007 value = view.getAutofillValue();
1008 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001009
Felipe Leme0aa4c502017-04-26 12:36:01 -07001010 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001011 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001012 }
1013
Felipe Lemebab851c2017-02-03 18:45:08 -08001014 /**
Felipe Leme6dcec872017-05-25 11:24:23 -07001015 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -08001016 *
Felipe Leme6dcec872017-05-25 11:24:23 -07001017 * @param view the virtual view parent.
1018 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -08001019 * @param value new value of the child.
1020 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001021 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -07001022 if (!hasAutofillFeature()) {
1023 return;
1024 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001025 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001026 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001027 return;
1028 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001029
Felipe Leme6dcec872017-05-25 11:24:23 -07001030 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -07001031 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001032 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001033 }
1034
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001035
1036 /**
1037 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
1038 *
1039 * @hide
1040 */
1041 public void notifyViewClicked(View view) {
1042 final AutofillId id = view.getAutofillId();
1043
1044 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
1045
1046 synchronized (mLock) {
1047 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
1048 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
1049 commitLocked();
1050 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
1051 mContext.getPackageName());
1052 }
1053 }
1054 }
1055
1056 /**
1057 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
1058 *
1059 * @hide
1060 */
1061 public void onActivityFinished() {
1062 if (!hasAutofillFeature()) {
1063 return;
1064 }
1065 synchronized (mLock) {
1066 if (mSaveOnFinish) {
Felipe Leme601d2202017-11-17 08:21:23 -08001067 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001068 commitLocked();
1069 } else {
1070 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
1071 cancelLocked();
1072 }
1073 }
1074 }
1075
Felipe Lemebab851c2017-02-03 18:45:08 -08001076 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001077 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -08001078 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001079 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1080 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1081 * that represent the HTML elements, it should call this method after the form is submitted and
1082 * another page is rendered.
1083 *
1084 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1085 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001086 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001087 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001088 if (!hasAutofillFeature()) {
1089 return;
1090 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001091 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001092 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001093 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001094 }
1095
Andreas Gampe3f24e692018-02-05 13:24:28 -08001096 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001097 private void commitLocked() {
1098 if (!mEnabled && !isActiveLocked()) {
1099 return;
1100 }
1101 finishSessionLocked();
1102 }
1103
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001104 /**
1105 * Called to indicate the current autofill context should be cancelled.
1106 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001107 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1108 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1109 * that represent the HTML elements, it should call this method if the user does not post the
1110 * form but moves to another form in this page.
1111 *
1112 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1113 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001114 */
1115 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001116 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001117 if (!hasAutofillFeature()) {
1118 return;
1119 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001120 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001121 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001122 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001123 }
1124
Andreas Gampe3f24e692018-02-05 13:24:28 -08001125 @GuardedBy("mLock")
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001126 private void cancelLocked() {
1127 if (!mEnabled && !isActiveLocked()) {
1128 return;
1129 }
1130 cancelSessionLocked();
1131 }
1132
Svet Ganovf965b872017-04-28 16:34:02 -07001133 /** @hide */
1134 public void disableOwnedAutofillServices() {
1135 disableAutofillServices();
1136 }
1137
Svet Ganovf20a0372017-04-10 17:08:05 -07001138 /**
1139 * If the app calling this API has enabled autofill services they
1140 * will be disabled.
1141 */
Svet Ganovf965b872017-04-28 16:34:02 -07001142 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001143 if (!hasAutofillFeature()) {
1144 return;
1145 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001146 try {
1147 mService.disableOwnedAutofillServices(mContext.getUserId());
1148 } catch (RemoteException e) {
1149 throw e.rethrowFromSystemServer();
1150 }
1151 }
1152
Felipe Lemedb041182017-04-21 17:33:38 -07001153 /**
1154 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1155 * enabled for the current user, or {@code false} otherwise.
1156 */
1157 public boolean hasEnabledAutofillServices() {
1158 if (mService == null) return false;
1159
1160 try {
1161 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1162 } catch (RemoteException e) {
1163 throw e.rethrowFromSystemServer();
1164 }
1165 }
1166
1167 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001168 * Returns the component name of the {@link AutofillService} that is enabled for the current
1169 * user.
1170 */
1171 @Nullable
1172 public ComponentName getAutofillServiceComponentName() {
1173 if (mService == null) return null;
1174
1175 try {
1176 return mService.getAutofillServiceComponentName();
1177 } catch (RemoteException e) {
1178 throw e.rethrowFromSystemServer();
1179 }
1180 }
1181
1182 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001183 * Gets the id of the {@link UserData} used for
1184 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1185 *
1186 * <p>This method is useful when the service must check the status of the {@link UserData} in
1187 * the device without fetching the whole object.
1188 *
1189 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1190 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1191 * the user.
1192 *
1193 * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
1194 * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
1195 * service for the user.
1196 */
1197 @Nullable public String getUserDataId() {
1198 try {
1199 return mService.getUserDataId();
1200 } catch (RemoteException e) {
1201 e.rethrowFromSystemServer();
1202 return null;
1203 }
1204 }
1205
1206 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001207 * Gets the user data used for
1208 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001209 *
Felipe Leme27f45732017-12-22 09:05:22 -08001210 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1211 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1212 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001213 *
Felipe Leme452886a2017-11-27 13:09:13 -08001214 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1215 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001216 */
Felipe Leme452886a2017-11-27 13:09:13 -08001217 @Nullable public UserData getUserData() {
1218 try {
1219 return mService.getUserData();
1220 } catch (RemoteException e) {
1221 e.rethrowFromSystemServer();
1222 return null;
1223 }
1224 }
1225
1226 /**
Felipe Lemef0baef742018-01-26 14:39:39 -08001227 * Sets the {@link UserData} used for
Felipe Leme78172e72017-12-08 17:01:15 -08001228 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001229 *
1230 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1231 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1232 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001233 */
Felipe Leme452886a2017-11-27 13:09:13 -08001234 public void setUserData(@Nullable UserData userData) {
1235 try {
1236 mService.setUserData(userData);
1237 } catch (RemoteException e) {
1238 e.rethrowFromSystemServer();
1239 }
1240 }
1241
1242 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001243 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1244 * enabled.
1245 *
1246 * <p>As field classification is an expensive operation, it could be disabled, either
1247 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1248 * permanently (for example, because the device is a low-level device).
1249 *
1250 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1251 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1252 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001253 */
Felipe Leme329d0402017-12-06 09:22:43 -08001254 public boolean isFieldClassificationEnabled() {
1255 try {
1256 return mService.isFieldClassificationEnabled();
1257 } catch (RemoteException e) {
1258 e.rethrowFromSystemServer();
1259 return false;
1260 }
1261 }
1262
1263 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001264 * Gets the name of the default algorithm used for
1265 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1266 *
1267 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1268 * set.
1269 *
1270 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1271 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1272 * the user.
1273 */
1274 @Nullable
1275 public String getDefaultFieldClassificationAlgorithm() {
1276 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001277 return mService.getDefaultFieldClassificationAlgorithm();
Felipe Leme27f45732017-12-22 09:05:22 -08001278 } catch (RemoteException e) {
1279 e.rethrowFromSystemServer();
1280 return null;
1281 }
1282 }
1283
1284 /**
1285 * Gets the name of all algorithms currently available for
1286 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1287 *
1288 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
Felipe Lemebc055b02018-01-05 17:04:10 -08001289 * and it returns an empty list if the caller currently doesn't have an enabled autofill service
1290 * for the user.
Felipe Leme27f45732017-12-22 09:05:22 -08001291 */
1292 @NonNull
1293 public List<String> getAvailableFieldClassificationAlgorithms() {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001294 final String[] algorithms;
Felipe Leme27f45732017-12-22 09:05:22 -08001295 try {
Felipe Lemee4ac7402018-01-16 19:37:00 -08001296 algorithms = mService.getAvailableFieldClassificationAlgorithms();
1297 return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
Felipe Leme27f45732017-12-22 09:05:22 -08001298 } catch (RemoteException e) {
1299 e.rethrowFromSystemServer();
1300 return null;
1301 }
1302 }
1303
1304 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001305 * Returns {@code true} if autofill is supported by the current device and
1306 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001307 *
1308 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1309 * <ol>
1310 * <li>Low-end devices.
1311 * <li>Device policy rules that forbid its usage.
1312 * </ol>
1313 */
1314 public boolean isAutofillSupported() {
1315 if (mService == null) return false;
1316
1317 try {
1318 return mService.isServiceSupported(mContext.getUserId());
1319 } catch (RemoteException e) {
1320 throw e.rethrowFromSystemServer();
1321 }
1322 }
1323
Felipe Leme637e05e2017-12-06 12:09:37 -08001324 // Note: don't need to use locked suffix because mContext is final.
1325 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001326 final AutofillClient client = mContext.getAutofillClient();
1327 if (client == null && sDebug) {
1328 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1329 + mContext);
1330 }
1331 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001332 }
1333
1334 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001335 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001336 if (!hasAutofillFeature()) {
1337 return;
1338 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001339 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001340 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1341 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1342 // service set the extra and returned RESULT_CANCELED...
1343
Felipe Leme9f9ee252017-04-27 13:56:22 -07001344 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001345
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001346 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001347 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001348 return;
1349 }
Dake Gu67decfa2017-12-27 11:48:08 -08001350 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1351 // of app activity. We enforce enter event to re-show fill ui in such case.
1352 // CTS example:
1353 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1354 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1355 if (!mOnInvisibleCalled && focusView != null
1356 && focusView.canNotifyAutofillEnterExitEvent()) {
1357 notifyViewExitedLocked(focusView);
1358 notifyViewEnteredLocked(focusView, 0);
1359 }
1360 if (data == null) {
1361 // data is set to null when result is not RESULT_OK
1362 return;
1363 }
1364
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001365 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1366 final Bundle responseData = new Bundle();
1367 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001368 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1369 if (newClientState != null) {
1370 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1371 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001372 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001373 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1374 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001375 } catch (RemoteException e) {
1376 Log.e(TAG, "Error delivering authentication result", e);
1377 }
Svet Ganov782043c2017-02-11 00:52:02 +00001378 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001379 }
1380
Felipe Leme640f30a2017-03-06 15:44:06 -08001381 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001382 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001383 }
1384
Felipe Leme6dcec872017-05-25 11:24:23 -07001385 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001386 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001387 }
1388
Andreas Gampe3f24e692018-02-05 13:24:28 -08001389 @GuardedBy("mLock")
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001390 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1391 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001392 if (sVerbose) {
1393 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
felipealfd3f8f62018-02-07 11:49:07 +01001394 + ", flags=" + flags + ", state=" + getStateAsStringLocked()
1395 + ", compatMode=" + isCompatibilityModeEnabledLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001396 }
Felipe Leme3103c632017-12-18 15:05:14 -08001397 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001398 if (sVerbose) {
1399 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001400 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001401 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001402 return;
1403 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001404 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001405 final AutofillClient client = getClient();
felipealfd3f8f62018-02-07 11:49:07 +01001406 if (client == null) return; // NOTE: getClient() already logged it..
Felipe Leme637e05e2017-12-06 12:09:37 -08001407
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001408 mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001409 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001410 mCallback != null, flags, client.autofillClientGetComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001411 if (mSessionId != NO_SESSION) {
1412 mState = STATE_ACTIVE;
1413 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001414 client.autofillClientResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001415 } catch (RemoteException e) {
1416 throw e.rethrowFromSystemServer();
1417 }
1418 }
1419
Andreas Gampe3f24e692018-02-05 13:24:28 -08001420 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001421 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001422 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001423
1424 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001425
Svet Ganov782043c2017-02-11 00:52:02 +00001426 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001427 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001428 } catch (RemoteException e) {
1429 throw e.rethrowFromSystemServer();
1430 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001431
Felipe Lemec24a56a2017-08-03 14:27:57 -07001432 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001433 }
1434
Andreas Gampe3f24e692018-02-05 13:24:28 -08001435 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001436 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001437 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001438
1439 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001440
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001441 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001442 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001443 } catch (RemoteException e) {
1444 throw e.rethrowFromSystemServer();
1445 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001446
Svet Ganov48f10a22017-04-26 18:49:30 -07001447 resetSessionLocked();
1448 }
1449
Andreas Gampe3f24e692018-02-05 13:24:28 -08001450 @GuardedBy("mLock")
Svet Ganov48f10a22017-04-26 18:49:30 -07001451 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001452 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001453 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001454 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001455 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001456 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001457 }
1458
Andreas Gampe3f24e692018-02-05 13:24:28 -08001459 @GuardedBy("mLock")
Felipe Leme0aa4c502017-04-26 12:36:01 -07001460 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1461 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001462 if (sVerbose && action != ACTION_VIEW_EXITED) {
1463 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1464 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001465 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001466 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1467
Felipe Leme3461d3c2017-01-19 08:54:55 -08001468 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001469 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001470 final AutofillClient client = getClient();
1471 if (client == null) return; // NOTE: getClient() already logd it..
1472
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001473 final int newId = mService.updateOrRestartSession(
1474 client.autofillClientGetActivityToken(),
Felipe Leme7f33cd32017-05-11 10:10:49 -07001475 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001476 mCallback != null, flags, client.autofillClientGetComponentName(),
1477 mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001478 if (newId != mSessionId) {
1479 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1480 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001481 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001482 client.autofillClientResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001483 }
1484 } else {
1485 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1486 mContext.getUserId());
1487 }
1488
Felipe Leme3461d3c2017-01-19 08:54:55 -08001489 } catch (RemoteException e) {
1490 throw e.rethrowFromSystemServer();
1491 }
1492 }
Svet Ganov782043c2017-02-11 00:52:02 +00001493
Andreas Gampe3f24e692018-02-05 13:24:28 -08001494 @GuardedBy("mLock")
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001495 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001496 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001497 return;
1498 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001499
Svet Ganov782043c2017-02-11 00:52:02 +00001500 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001501 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001502 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001503 final int userId = mContext.getUserId();
1504 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001505 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1506 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1507 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001508 final IAutoFillManager service = mService;
1509 final IAutoFillManagerClient serviceClient = mServiceClient;
1510 mServiceClientCleaner = Cleaner.create(this, () -> {
1511 try {
1512 service.removeClient(serviceClient, userId);
1513 } catch (RemoteException e) {
1514 }
1515 });
Svet Ganov782043c2017-02-11 00:52:02 +00001516 } catch (RemoteException e) {
1517 throw e.rethrowFromSystemServer();
1518 }
1519 }
1520 }
1521
Felipe Lemee6010f22017-03-03 11:19:51 -08001522 /**
1523 * Registers a {@link AutofillCallback} to receive autofill events.
1524 *
1525 * @param callback callback to receive events.
1526 */
1527 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001528 if (!hasAutofillFeature()) {
1529 return;
1530 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001531 synchronized (mLock) {
1532 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001533
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001534 final boolean hadCallback = mCallback != null;
1535 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001536
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001537 if (!hadCallback) {
1538 try {
1539 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1540 } catch (RemoteException e) {
1541 throw e.rethrowFromSystemServer();
1542 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001543 }
1544 }
1545 }
1546
1547 /**
1548 * Unregisters a {@link AutofillCallback} to receive autofill events.
1549 *
1550 * @param callback callback to stop receiving events.
1551 */
1552 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001553 if (!hasAutofillFeature()) {
1554 return;
1555 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001556 synchronized (mLock) {
1557 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001558
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001559 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001560
Felipe Lemee6010f22017-03-03 11:19:51 -08001561 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001562 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001563 } catch (RemoteException e) {
1564 throw e.rethrowFromSystemServer();
1565 }
1566 }
1567 }
1568
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001569 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1570 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1571 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001572 if (anchor == null) {
1573 return;
1574 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001575
1576 AutofillCallback callback = null;
1577 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001578 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001579 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001580
1581 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001582 if (client.autofillClientRequestShowFillUi(anchor, width, height,
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001583 anchorBounds, presenter) && mCallback != null) {
1584 callback = mCallback;
1585 }
1586 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001587 }
1588 }
1589
1590 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001591 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001592 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001593 AutofillCallback.EVENT_INPUT_SHOWN);
1594 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001595 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001596 }
1597 }
1598 }
1599
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001600 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1601 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001602 synchronized (mLock) {
1603 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001604 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001605 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001606 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1607 // before onAuthenticationResult()
1608 mOnInvisibleCalled = false;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001609 client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001610 }
1611 }
1612 }
1613 }
1614
Felipe Leme51e29da2017-10-24 14:03:10 -07001615 /** @hide */
1616 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1617 /** @hide */
1618 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1619 /** @hide */
1620 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1621 /** @hide */
1622 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1623 /** @hide */
1624 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1625
1626 private void setState(int flags) {
1627 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001628 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001629 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1630 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001631 // Reset the session state
1632 resetSessionLocked();
1633 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001634 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001635 // Reset connection to system
1636 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001637 if (mServiceClientCleaner != null) {
1638 mServiceClientCleaner.clean();
1639 mServiceClientCleaner = null;
1640 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001641 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001642 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001643 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1644 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001645 }
1646
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001647 /**
1648 * Sets a view as autofilled if the current value is the {code targetValue}.
1649 *
1650 * @param view The view that is to be autofilled
1651 * @param targetValue The value we want to fill into view
1652 */
1653 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1654 AutofillValue currentValue = view.getAutofillValue();
1655 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001656 synchronized (mLock) {
1657 if (mLastAutofilledData == null) {
1658 mLastAutofilledData = new ParcelableMap(1);
1659 }
1660 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001661 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001662 view.setAutofilled(true);
1663 }
1664 }
1665
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001666 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001667 synchronized (mLock) {
1668 if (sessionId != mSessionId) {
1669 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001670 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001671
Felipe Leme637e05e2017-12-06 12:09:37 -08001672 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001673 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001674 return;
1675 }
1676
1677 final int itemCount = ids.size();
1678 int numApplied = 0;
1679 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001680 final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
1681 Helper.toArray(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001682
1683 for (int i = 0; i < itemCount; i++) {
1684 final AutofillId id = ids.get(i);
1685 final AutofillValue value = values.get(i);
1686 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001687 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001688 if (view == null) {
1689 Log.w(TAG, "autofill(): no View with id " + viewId);
1690 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001691 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001692 if (id.isVirtual()) {
1693 if (virtualValues == null) {
1694 // Most likely there will be just one view with virtual children.
1695 virtualValues = new ArrayMap<>(1);
1696 }
1697 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1698 if (valuesByParent == null) {
1699 // We don't know the size yet, but usually it will be just a few fields...
1700 valuesByParent = new SparseArray<>(5);
1701 virtualValues.put(view, valuesByParent);
1702 }
1703 valuesByParent.put(id.getVirtualChildId(), value);
1704 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001705 // Mark the view as to be autofilled with 'value'
1706 if (mLastAutofilledData == null) {
1707 mLastAutofilledData = new ParcelableMap(itemCount - i);
1708 }
1709 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001710
1711 view.autofill(value);
1712
1713 // Set as autofilled if the values match now, e.g. when the value was updated
1714 // synchronously.
1715 // If autofill happens async, the view is set to autofilled in
1716 // notifyValueChanged.
1717 setAutofilledIfValuesIs(view, value);
1718
1719 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001720 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001721 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001722
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001723 if (virtualValues != null) {
1724 for (int i = 0; i < virtualValues.size(); i++) {
1725 final View parent = virtualValues.keyAt(i);
1726 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1727 parent.autofill(childrenValues);
1728 numApplied += childrenValues.size();
1729 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001730 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001731
Felipe Lemeb22d6352017-09-08 20:03:53 -07001732 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1733 .setPackageName(mContext.getPackageName())
1734 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1735 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001736 mMetricsLogger.write(log);
1737 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001738 }
1739
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001740 /**
1741 * Set the tracked views.
1742 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001743 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001744 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001745 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001746 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001747 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001748 */
Felipe Leme27e20222017-05-18 15:24:11 -07001749 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001750 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1751 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001752 synchronized (mLock) {
1753 if (mEnabled && mSessionId == sessionId) {
1754 if (saveOnAllViewsInvisible) {
1755 mTrackedViews = new TrackedViews(trackedIds);
1756 } else {
1757 mTrackedViews = null;
1758 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001759 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001760 if (fillableIds != null) {
1761 if (mFillableIds == null) {
1762 mFillableIds = new ArraySet<>(fillableIds.length);
1763 }
1764 for (AutofillId id : fillableIds) {
1765 mFillableIds.add(id);
1766 }
1767 if (sVerbose) {
1768 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1769 + ", mFillableIds" + mFillableIds);
1770 }
1771 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001772
1773 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1774 // Turn off trigger on previous view id.
1775 setNotifyOnClickLocked(mSaveTriggerId, false);
1776 }
1777
1778 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1779 // Turn on trigger on new view id.
1780 mSaveTriggerId = saveTriggerId;
1781 setNotifyOnClickLocked(mSaveTriggerId, true);
1782 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001783 }
1784 }
1785 }
1786
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001787 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1788 final View view = findView(id);
1789 if (view == null) {
1790 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1791 return;
1792 }
1793 view.setNotifyAutofillManagerOnClick(notify);
1794 }
1795
Felipe Lemec24a56a2017-08-03 14:27:57 -07001796 private void setSaveUiState(int sessionId, boolean shown) {
1797 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1798 synchronized (mLock) {
1799 if (mSessionId != NO_SESSION) {
1800 // Race condition: app triggered a new session after the previous session was
1801 // finished but before server called setSaveUiState() - need to cancel the new
1802 // session to avoid further inconsistent behavior.
1803 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1804 + ") called on existing session " + mSessionId + "; cancelling it");
1805 cancelSessionLocked();
1806 }
1807 if (shown) {
1808 mSessionId = sessionId;
1809 mState = STATE_SHOWING_SAVE_UI;
1810 } else {
1811 mSessionId = NO_SESSION;
1812 mState = STATE_UNKNOWN;
1813 }
1814 }
1815 }
1816
Felipe Leme650f7ab2017-09-19 13:08:24 -07001817 /**
1818 * Marks the state of the session as finished.
1819 *
1820 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001821 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1822 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1823 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001824 */
1825 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001826 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001827 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001828 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001829 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001830 }
1831 }
1832
Felipe Leme27e20222017-05-18 15:24:11 -07001833 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001834 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001835 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001836 if (anchor == null) {
1837 return;
1838 }
Felipe Leme27e20222017-05-18 15:24:11 -07001839 requestHideFillUi(id, anchor);
1840 }
1841
1842 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001843
1844 AutofillCallback callback = null;
1845 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001846 // We do not check the session id for two reasons:
1847 // 1. If local and remote session id are off sync the UI would be stuck shown
1848 // 2. There is a race between the user state being destroyed due the fill
1849 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001850 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001851 if (client != null) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001852 if (client.autofillClientRequestHideFillUi() && mCallback != null) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001853 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001854 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001855 }
1856 }
1857
1858 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001859 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001860 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001861 AutofillCallback.EVENT_INPUT_HIDDEN);
1862 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001863 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001864 }
1865 }
1866 }
1867
Felipe Leme17292d12017-10-24 14:03:10 -07001868 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001869 if (sVerbose) {
1870 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001871 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001872 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001873 final View anchor = findView(id);
1874 if (anchor == null) {
1875 return;
1876 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001877
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001878 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001879 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001880 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001881 callback = mCallback;
1882 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001883 }
1884
1885 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001886 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001887 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001888 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1889 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001890 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001891 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001892 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001893
Felipe Leme17292d12017-10-24 14:03:10 -07001894 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001895 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001896 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001897 }
1898 }
1899
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001900 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001901 * Find a single view by its id.
1902 *
1903 * @param autofillId The autofill id of the view
1904 *
1905 * @return The view or {@code null} if view was not found
1906 */
1907 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001908 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001909 if (client != null) {
1910 return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
Felipe Lemee6010f22017-03-03 11:19:51 -08001911 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08001912 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001913 }
1914
Felipe Lemebb810922017-04-25 15:54:06 -07001915 /** @hide */
1916 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001917 return mService != null;
1918 }
1919
Felipe Lemec24a56a2017-08-03 14:27:57 -07001920 /** @hide */
1921 public void onPendingSaveUi(int operation, IBinder token) {
1922 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1923
1924 synchronized (mLock) {
1925 try {
1926 mService.onPendingSaveUi(operation, token);
1927 } catch (RemoteException e) {
1928 e.rethrowFromSystemServer();
1929 }
1930 }
1931 }
1932
1933 /** @hide */
1934 public void dump(String outerPrefix, PrintWriter pw) {
1935 pw.print(outerPrefix); pw.println("AutofillManager:");
1936 final String pfx = outerPrefix + " ";
1937 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001938 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001939 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001940 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001941 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1942 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1943 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08001944 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001945 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1946 pw.print(pfx); pw.print("tracked views: ");
1947 if (mTrackedViews == null) {
1948 pw.println("null");
1949 } else {
1950 final String pfx2 = pfx + " ";
1951 pw.println();
1952 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1953 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1954 }
1955 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001956 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1957 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
felipealfd3f8f62018-02-07 11:49:07 +01001958 pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
1959 isCompatibilityModeEnabledLocked());
Felipe Leme51e29da2017-10-24 14:03:10 -07001960 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1961 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001962 }
1963
Andreas Gampe3f24e692018-02-05 13:24:28 -08001964 @GuardedBy("mLock")
Felipe Lemec7b45292017-09-19 09:06:20 -07001965 private String getStateAsStringLocked() {
1966 switch (mState) {
1967 case STATE_UNKNOWN:
1968 return "STATE_UNKNOWN";
1969 case STATE_ACTIVE:
1970 return "STATE_ACTIVE";
1971 case STATE_FINISHED:
1972 return "STATE_FINISHED";
1973 case STATE_SHOWING_SAVE_UI:
1974 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001975 case STATE_DISABLED_BY_SERVICE:
1976 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001977 default:
1978 return "INVALID:" + mState;
1979 }
1980 }
1981
Andreas Gampe3f24e692018-02-05 13:24:28 -08001982 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -07001983 private boolean isActiveLocked() {
1984 return mState == STATE_ACTIVE;
1985 }
1986
Andreas Gampe3f24e692018-02-05 13:24:28 -08001987 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08001988 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07001989 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001990 }
1991
Andreas Gampe3f24e692018-02-05 13:24:28 -08001992 @GuardedBy("mLock")
Felipe Leme3103c632017-12-18 15:05:14 -08001993 private boolean isFinishedLocked() {
1994 return mState == STATE_FINISHED;
1995 }
1996
Felipe Leme9876a6f2017-05-30 15:47:28 -07001997 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001998 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07001999 if (client == null) {
2000 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
2001 return;
2002 }
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002003 client.autofillClientRunOnUiThread(runnable);
2004 }
2005
2006 /**
2007 * Implementation of the accessibility based compatibility.
2008 */
2009 private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
2010 @GuardedBy("mLock")
2011 private final Rect mFocusedBounds = new Rect();
2012 @GuardedBy("mLock")
2013 private final Rect mTempBounds = new Rect();
2014
2015 @GuardedBy("mLock")
2016 private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2017 @GuardedBy("mLock")
2018 private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2019
2020 // Need to report a fake service in case a11y clients check the service list
2021 @NonNull
2022 @GuardedBy("mLock")
2023 AccessibilityServiceInfo mCompatServiceInfo;
2024
2025 CompatibilityBridge() {
2026 final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
2027 am.setAccessibilityPolicy(this);
2028 }
2029
2030 private AccessibilityServiceInfo getCompatServiceInfo() {
2031 synchronized (mLock) {
2032 if (mCompatServiceInfo != null) {
2033 return mCompatServiceInfo;
2034 }
2035 final Intent intent = new Intent();
2036 intent.setComponent(new ComponentName("android",
2037 "com.android.server.autofill.AutofillCompatAccessibilityService"));
2038 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
2039 intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
2040 try {
2041 mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
2042 } catch (XmlPullParserException | IOException e) {
2043 Log.e(TAG, "Cannot find compat autofill service:" + intent);
2044 throw new IllegalStateException("Cannot find compat autofill service");
2045 }
2046 return mCompatServiceInfo;
2047 }
2048 }
2049
2050 @Override
2051 public boolean isEnabled(boolean accessibilityEnabled) {
2052 return true;
2053 }
2054
2055 @Override
2056 public int getRelevantEventTypes(int relevantEventTypes) {
2057 return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
2058 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
2059 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
2060 }
2061
2062 @Override
2063 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
2064 List<AccessibilityServiceInfo> installedServices) {
2065 if (installedServices == null) {
2066 installedServices = new ArrayList<>();
2067 }
2068 installedServices.add(getCompatServiceInfo());
2069 return installedServices;
2070 }
2071
2072 @Override
2073 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
2074 int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
2075 if (enabledService == null) {
2076 enabledService = new ArrayList<>();
2077 }
2078 enabledService.add(getCompatServiceInfo());
2079 return enabledService;
2080 }
2081
2082 @Override
2083 public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
2084 boolean accessibilityEnabled, int relevantEventTypes) {
2085 switch (event.getEventType()) {
2086 case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
2087 synchronized (mLock) {
2088 if (mFocusedWindowId == event.getWindowId()
2089 && mFocusedNodeId == event.getSourceNodeId()) {
2090 return event;
2091 }
2092 if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
2093 && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
2094 notifyViewExited(mFocusedWindowId, mFocusedNodeId);
2095 mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
2096 mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2097 mFocusedBounds.set(0, 0, 0, 0);
2098 }
2099 final int windowId = event.getWindowId();
2100 final long nodeId = event.getSourceNodeId();
2101 if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
2102 mFocusedWindowId = windowId;
2103 mFocusedNodeId = nodeId;
2104 }
2105 }
2106 } break;
2107
2108 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
2109 synchronized (mLock) {
2110 if (mFocusedWindowId == event.getWindowId()
2111 && mFocusedNodeId == event.getSourceNodeId()) {
2112 notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
2113 }
2114 }
2115 } break;
2116
2117 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
2118 final AutofillClient client = getClient();
2119 if (client != null) {
2120 synchronized (mLock) {
2121 if (client.autofillClientIsFillUiShowing()) {
2122 notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
2123 }
2124 updateTrackedViewsLocked();
2125 }
2126 }
2127 } break;
2128 }
2129
2130 return accessibilityEnabled ? event : null;
2131 }
2132
2133 private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
2134 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2135 if (!isVirtualNode(virtualId)) {
2136 return false;
2137 }
2138 final View view = findViewByAccessibilityId(windowId, nodeId);
2139 if (view == null) {
2140 return false;
2141 }
2142 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2143 if (node == null) {
2144 return false;
2145 }
2146 if (!node.isEditable()) {
2147 return false;
2148 }
2149 final Rect newBounds = mTempBounds;
2150 node.getBoundsInScreen(newBounds);
2151 if (newBounds.equals(focusedBounds)) {
2152 return false;
2153 }
2154 focusedBounds.set(newBounds);
2155 AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
2156 return true;
2157 }
2158
2159 private void notifyViewExited(int windowId, long nodeId) {
2160 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2161 if (!isVirtualNode(virtualId)) {
2162 return;
2163 }
2164 final View view = findViewByAccessibilityId(windowId, nodeId);
2165 if (view == null) {
2166 return;
2167 }
2168 AutofillManager.this.notifyViewExited(view, virtualId);
2169 }
2170
2171 private void notifyValueChanged(int windowId, long nodeId) {
2172 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
2173 if (!isVirtualNode(virtualId)) {
2174 return;
2175 }
2176 final View view = findViewByAccessibilityId(windowId, nodeId);
2177 if (view == null) {
2178 return;
2179 }
2180 final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
2181 if (node == null) {
2182 return;
2183 }
2184 AutofillManager.this.notifyValueChanged(view, virtualId,
2185 AutofillValue.forText(node.getText()));
2186 }
2187
Andreas Gampe3f24e692018-02-05 13:24:28 -08002188 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002189 private void updateTrackedViewsLocked() {
2190 if (mTrackedViews != null) {
2191 mTrackedViews.onVisibleForAutofillChangedLocked();
2192 }
2193 }
2194
2195 private View findViewByAccessibilityId(int windowId, long nodeId) {
2196 final AutofillClient client = getClient();
2197 if (client == null) {
2198 return null;
2199 }
2200 final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
2201 return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
2202 }
2203
2204 private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
2205 final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
2206 if (provider == null) {
2207 return null;
2208 }
2209 return provider.createAccessibilityNodeInfo(virtualId);
2210 }
2211
2212 private boolean isVirtualNode(int nodeId) {
2213 return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
2214 && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
2215 }
Felipe Leme9876a6f2017-05-30 15:47:28 -07002216 }
2217
Felipe Lemee6010f22017-03-03 11:19:51 -08002218 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002219 * View tracking information. Once all tracked views become invisible the session is finished.
2220 */
2221 private class TrackedViews {
2222 /** Visible tracked views */
2223 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
2224
2225 /** Invisible tracked views */
2226 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
2227
2228 /**
2229 * Check if set is null or value is in set.
2230 *
2231 * @param set The set or null (== empty set)
2232 * @param value The value that might be in the set
2233 *
2234 * @return {@code true} iff set is not empty and value is in set
2235 */
Felipe Leme27e20222017-05-18 15:24:11 -07002236 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002237 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
2238 return set != null && set.contains(value);
2239 }
2240
2241 /**
2242 * Add a value to a set. If set is null, create a new set.
2243 *
2244 * @param set The set or null (== empty set)
2245 * @param valueToAdd The value to add
2246 *
2247 * @return The set including the new value. If set was {@code null}, a set containing only
2248 * the new value.
2249 */
Felipe Leme27e20222017-05-18 15:24:11 -07002250 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002251 @NonNull
2252 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
2253 if (set == null) {
2254 set = new ArraySet<>(1);
2255 }
2256
2257 set.add(valueToAdd);
2258
2259 return set;
2260 }
2261
2262 /**
2263 * Remove a value from a set.
2264 *
2265 * @param set The set or null (== empty set)
2266 * @param valueToRemove The value to remove
2267 *
2268 * @return The set without the removed value. {@code null} if set was null, or is empty
2269 * after removal.
2270 */
Felipe Leme27e20222017-05-18 15:24:11 -07002271 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002272 @Nullable
2273 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
2274 if (set == null) {
2275 return null;
2276 }
2277
2278 set.remove(valueToRemove);
2279
2280 if (set.isEmpty()) {
2281 return null;
2282 }
2283
2284 return set;
2285 }
2286
2287 /**
2288 * Set the tracked views.
2289 *
2290 * @param trackedIds The views to be tracked
2291 */
Felipe Leme27e20222017-05-18 15:24:11 -07002292 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08002293 final AutofillClient client = getClient();
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002294 if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002295 final boolean[] isVisible;
2296
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002297 if (client.autofillClientIsVisibleForAutofill()) {
2298 isVisible = client.autofillClientGetViewVisibility(trackedIds);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002299 } else {
2300 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07002301 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002302 }
2303
Felipe Leme27e20222017-05-18 15:24:11 -07002304 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002305 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002306 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002307
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002308 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002309 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002310 } else {
2311 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2312 }
2313 }
2314 }
2315
Felipe Leme9f9ee252017-04-27 13:56:22 -07002316 if (sVerbose) {
2317 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002318 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2319 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2320 }
2321
2322 if (mVisibleTrackedIds == null) {
2323 finishSessionLocked();
2324 }
2325 }
2326
2327 /**
2328 * Called when a {@link View view's} visibility changes.
2329 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002330 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002331 * @param isVisible visible if the view is visible in the view hierarchy.
2332 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002333 @GuardedBy("mLock")
Dake Gu67decfa2017-12-27 11:48:08 -08002334 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002335 if (sDebug) {
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002336 Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002337 + isVisible);
2338 }
2339
Dake Gu67decfa2017-12-27 11:48:08 -08002340 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002341 if (isVisible) {
2342 if (isInSet(mInvisibleTrackedIds, id)) {
2343 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2344 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2345 }
2346 } else {
2347 if (isInSet(mVisibleTrackedIds, id)) {
2348 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2349 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2350 }
2351 }
2352 }
2353
2354 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002355 if (sVerbose) {
2356 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2357 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002358 finishSessionLocked();
2359 }
2360 }
2361
2362 /**
2363 * Called once the client becomes visible.
2364 *
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002365 * @see AutofillClient#autofillClientIsVisibleForAutofill()
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002366 */
Andreas Gampe3f24e692018-02-05 13:24:28 -08002367 @GuardedBy("mLock")
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002368 void onVisibleForAutofillChangedLocked() {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002369 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002370 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002371 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002372 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2373 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2374 if (client != null) {
2375 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002376 final ArrayList<AutofillId> orderedInvisibleIds =
2377 new ArrayList<>(mInvisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002378 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2379 Helper.toArray(orderedInvisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002380
2381 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2382 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2383 final AutofillId id = orderedInvisibleIds.get(i);
2384 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002385 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2386
Felipe Leme9f9ee252017-04-27 13:56:22 -07002387 if (sDebug) {
2388 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002389 }
2390 } else {
2391 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2392 }
2393 }
2394 }
2395
2396 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002397 final ArrayList<AutofillId> orderedVisibleIds =
2398 new ArrayList<>(mVisibleTrackedIds);
Svetoslav Ganov24c90452017-12-27 15:17:14 -08002399 final boolean[] isVisible = client.autofillClientGetViewVisibility(
2400 Helper.toArray(orderedVisibleIds));
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002401
2402 final int numVisibleTrackedIds = orderedVisibleIds.size();
2403 for (int i = 0; i < numVisibleTrackedIds; i++) {
2404 final AutofillId id = orderedVisibleIds.get(i);
2405
2406 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002407 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2408 } else {
2409 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2410
Felipe Leme9f9ee252017-04-27 13:56:22 -07002411 if (sDebug) {
2412 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002413 }
2414 }
2415 }
2416 }
2417
2418 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2419 mVisibleTrackedIds = updatedVisibleTrackedIds;
2420 }
2421
2422 if (mVisibleTrackedIds == null) {
2423 finishSessionLocked();
2424 }
2425 }
2426 }
2427
2428 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002429 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002430 *
2431 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002432 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002433 */
2434 public abstract static class AutofillCallback {
2435
2436 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002437 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2438 EVENT_INPUT_SHOWN,
2439 EVENT_INPUT_HIDDEN,
2440 EVENT_INPUT_UNAVAILABLE
2441 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002442 @Retention(RetentionPolicy.SOURCE)
2443 public @interface AutofillEventType {}
2444
2445 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002446 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002447 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002448 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002449 * should be hidden upon receiving this event.
2450 */
2451 public static final int EVENT_INPUT_SHOWN = 1;
2452
2453 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002454 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002455 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002456 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002457 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2458 */
2459 public static final int EVENT_INPUT_HIDDEN = 2;
2460
2461 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002462 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002463 * autofill is not available.
2464 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002465 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002466 * to avoid flickering, it could shown it upon receiving this event.
2467 */
2468 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2469
2470 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002471 * Called after a change in the autofill state associated with a view.
2472 *
2473 * @param view view associated with the change.
2474 *
2475 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2476 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002477 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2478 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002479
2480 /**
2481 * Called after a change in the autofill state associated with a virtual view.
2482 *
2483 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002484 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002485 *
2486 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2487 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002488 public void onAutofillEvent(@NonNull View view, int virtualId,
2489 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002490 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002491 }
2492
Felipe Leme640f30a2017-03-06 15:44:06 -08002493 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2494 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002495
Felipe Leme640f30a2017-03-06 15:44:06 -08002496 AutofillManagerClient(AutofillManager autofillManager) {
2497 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002498 }
2499
2500 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002501 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002502 final AutofillManager afm = mAfm.get();
2503 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002504 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002505 }
2506 }
2507
2508 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002509 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002510 final AutofillManager afm = mAfm.get();
2511 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002512 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002513 }
2514 }
2515
2516 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002517 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2518 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002519 final AutofillManager afm = mAfm.get();
2520 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002521 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002522 }
2523 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002524
2525 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002526 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2527 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002528 final AutofillManager afm = mAfm.get();
2529 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002530 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2531 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002532 }
2533 }
2534
2535 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002536 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002537 final AutofillManager afm = mAfm.get();
2538 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002539 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002540 }
2541 }
2542
2543 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002544 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002545 final AutofillManager afm = mAfm.get();
2546 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002547 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002548 }
2549 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002550
2551 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002552 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002553 final AutofillManager afm = mAfm.get();
2554 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002555 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002556 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002557 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002558 } catch (IntentSender.SendIntentException e) {
2559 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2560 }
2561 });
2562 }
2563 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002564
2565 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002566 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002567 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2568 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002569 final AutofillManager afm = mAfm.get();
2570 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002571 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2572 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002573 }
2574 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002575
2576 @Override
2577 public void setSaveUiState(int sessionId, boolean shown) {
2578 final AutofillManager afm = mAfm.get();
2579 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002580 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2581 }
2582 }
2583
2584 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002585 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002586 final AutofillManager afm = mAfm.get();
2587 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002588 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002589 }
2590 }
Svet Ganov782043c2017-02-11 00:52:02 +00002591 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002592}