blob: de9b0d7dfa4abc881b4fa12197070056df7b3515 [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
Felipe Leme73fedac2017-05-12 09:52:07 -070019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
Felipe Leme9f9ee252017-04-27 13:56:22 -070020import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
Felipe Lemed633f072017-02-14 10:17:17 -080022
Felipe Lemee6010f22017-03-03 11:19:51 -080023import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080024import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080025import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.SystemService;
Felipe Leme17292d12017-10-24 14:03:10 -070027import android.content.ComponentName;
Felipe Leme3461d3c2017-01-19 08:54:55 -080028import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000029import android.content.Intent;
30import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080031import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070032import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000033import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070034import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000035import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080036import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070037import android.service.autofill.AutofillService;
38import android.service.autofill.FillEventHistory;
Felipe Leme452886a2017-11-27 13:09:13 -080039import android.service.autofill.UserData;
Felipe Leme4753bb02017-03-22 20:24:00 -070040import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070041import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080042import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070043import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080044import android.view.View;
45
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070046import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070047import com.android.internal.logging.MetricsLogger;
Felipe Lemeb22d6352017-09-08 20:03:53 -070048import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Felipe Leme637e05e2017-12-06 12:09:37 -080049import com.android.internal.util.Preconditions;
Felipe Leme4753bb02017-03-22 20:24:00 -070050
Felipe Lemec24a56a2017-08-03 14:27:57 -070051import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080052import java.lang.annotation.Retention;
53import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000054import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070055import java.util.ArrayList;
Felipe Leme27f45732017-12-22 09:05:22 -080056import java.util.Collections;
Svet Ganov782043c2017-02-11 00:52:02 +000057import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070058import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000059
Koji Fukuiccec6a62017-10-18 17:48:40 +090060// TODO: use java.lang.ref.Cleaner once Android supports Java 9
61import sun.misc.Cleaner;
62
Felipe Leme3461d3c2017-01-19 08:54:55 -080063/**
Felipe Leme744976e2017-07-31 11:34:14 -070064 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
65 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070066 *
Felipe Leme744976e2017-07-31 11:34:14 -070067 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
68 * activity context; the autofill context is created when one of the following methods is called for
69 * the first time in an activity context, and the current user has an enabled autofill service:
70 *
71 * <ul>
72 * <li>{@link #notifyViewEntered(View)}
73 * <li>{@link #notifyViewEntered(View, int, Rect)}
74 * <li>{@link #requestAutofill(View)}
75 * </ul>
76 *
77 * <p>Tipically, the context is automatically created when the first view of the activity is
78 * focused because {@code View.onFocusChanged()} indirectly calls
79 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
80 * explicitly create it (for example, a custom view developer could offer a contextual menu action
81 * in a text-field view to let users manually request autofill).
82 *
83 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
84 * that represents the view hierarchy by calling
85 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
86 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
87 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
88 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
89 * the hierarchy.
90 *
91 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
92 * parses it looking for views that can be autofilled. If the service finds such views, it returns
93 * a data structure to the Android System containing the following optional info:
94 *
95 * <ul>
96 * <li>Datasets used to autofill subsets of views in the activity.
97 * <li>Id of views that the service can save their values for future autofilling.
98 * </ul>
99 *
100 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700101 * UI associated with the view, when the view is focused on and is part of a dataset.
102 * The application can be notified when the UI is shown by registering an
Felipe Leme744976e2017-07-31 11:34:14 -0700103 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700104 * selects a dataset from the UI, all views present in the dataset are autofilled, through
Felipe Leme744976e2017-07-31 11:34:14 -0700105 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
106 *
107 * <p>When the service returns ids of savable views, the Android System keeps track of changes
108 * made to these views, so they can be used to determine if the autofill save UI is shown later.
109 *
110 * <p>The context is then finished when one of the following occurs:
111 *
112 * <ul>
113 * <li>{@link #commit()} is called or all savable views are gone.
114 * <li>{@link #cancel()} is called.
115 * </ul>
116 *
117 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700118 * 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 -0700119 * option to Save, the current value of the views is then sent to the autofill service.
120 *
121 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800122 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600123@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800124public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800125
Felipe Leme640f30a2017-03-06 15:44:06 -0800126 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800127
Svet Ganov782043c2017-02-11 00:52:02 +0000128 /**
129 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700130 *
Svet Ganov782043c2017-02-11 00:52:02 +0000131 * <p>
132 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000133 */
134 public static final String EXTRA_ASSIST_STRUCTURE =
135 "android.view.autofill.extra.ASSIST_STRUCTURE";
136
137 /**
138 * Intent extra: The result of an authentication operation. It is
139 * either a fully populated {@link android.service.autofill.FillResponse}
140 * or a fully populated {@link android.service.autofill.Dataset} if
141 * a response or a dataset is being authenticated respectively.
142 *
143 * <p>
144 * Type: {@link android.service.autofill.FillResponse} or a
145 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000146 */
147 public static final String EXTRA_AUTHENTICATION_RESULT =
148 "android.view.autofill.extra.AUTHENTICATION_RESULT";
149
Felipe Leme7320ca92017-03-29 15:09:54 -0700150 /**
151 * Intent extra: The optional extras provided by the
152 * {@link android.service.autofill.AutofillService}.
153 *
154 * <p>For example, when the service responds to a {@link
155 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
156 * a {@code FillResponse} that requires authentication, the Intent that launches the
157 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700158 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700159 *
Felipe Lemea9372382017-10-09 14:42:36 -0700160 * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
161 * can also add this bundle to the {@link Intent} set as the
162 * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
163 * so the bundle can be recovered later on
164 * {@link android.service.autofill.SaveRequest#getClientState()}.
165 *
Felipe Leme7320ca92017-03-29 15:09:54 -0700166 * <p>
167 * Type: {@link android.os.Bundle}
168 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700169 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600170 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700171
Felipe Lemec24a56a2017-08-03 14:27:57 -0700172
173 /** @hide */
174 public static final String EXTRA_RESTORE_SESSION_TOKEN =
175 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
176
177 private static final String SESSION_ID_TAG = "android:sessionId";
178 private static final String STATE_TAG = "android:state";
179 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
180
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700181
Felipe Leme0aa4c502017-04-26 12:36:01 -0700182 /** @hide */ public static final int ACTION_START_SESSION = 1;
183 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
184 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
185 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800186
Felipe Leme9f9ee252017-04-27 13:56:22 -0700187
188 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
189 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
190 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
191
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700192 /** Which bits in an authentication id are used for the dataset id */
193 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
194 /** How many bits in an authentication id are used for the dataset id */
195 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
196 /** @hide The index for an undefined data set */
197 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
198
199 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700200 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
201 *
202 * @hide
203 */
204 public static final int PENDING_UI_OPERATION_CANCEL = 1;
205
206 /**
207 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
208 *
209 * @hide
210 */
211 public static final int PENDING_UI_OPERATION_RESTORE = 2;
212
213 /**
214 * Initial state of the autofill context, set when there is no session (i.e., when
215 * {@link #mSessionId} is {@link #NO_SESSION}).
216 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700217 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
218 * the server.
219 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700220 * @hide
221 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700222 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700223
224 /**
225 * State where the autofill context hasn't been {@link #commit() finished} nor
226 * {@link #cancel() canceled} yet.
227 *
228 * @hide
229 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700230 public static final int STATE_ACTIVE = 1;
231
232 /**
233 * State where the autofill context was finished by the server because the autofill
Felipe Leme17292d12017-10-24 14:03:10 -0700234 * service could not autofill the activity.
Felipe Lemec7b45292017-09-19 09:06:20 -0700235 *
236 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
237 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
238 *
239 * @hide
240 */
241 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700242
243 /**
244 * State where the autofill context has been {@link #commit() finished} but the server still has
245 * a session because the Save UI hasn't been dismissed yet.
246 *
247 * @hide
248 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700249 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700250
251 /**
Felipe Leme17292d12017-10-24 14:03:10 -0700252 * State where the autofill is disabled because the service cannot autofill the activity at all.
253 *
254 * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
255 * (and {@link #requestAutofill(View, int, Rect)}).
256 *
257 * @hide
258 */
259 public static final int STATE_DISABLED_BY_SERVICE = 4;
260
261 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700262 * Makes an authentication id from a request id and a dataset id.
263 *
264 * @param requestId The request id.
265 * @param datasetId The dataset id.
266 * @return The authentication id.
267 * @hide
268 */
269 public static int makeAuthenticationId(int requestId, int datasetId) {
270 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
271 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
272 }
273
274 /**
275 * Gets the request id from an authentication id.
276 *
277 * @param authRequestId The authentication id.
278 * @return The request id.
279 * @hide
280 */
281 public static int getRequestIdFromAuthenticationId(int authRequestId) {
282 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
283 }
284
285 /**
286 * Gets the dataset id from an authentication id.
287 *
288 * @param authRequestId The authentication id.
289 * @return The dataset id.
290 * @hide
291 */
292 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
293 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
294 }
295
Felipe Leme4753bb02017-03-22 20:24:00 -0700296 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000297
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700298 /**
299 * There is currently no session running.
300 * {@hide}
301 */
302 public static final int NO_SESSION = Integer.MIN_VALUE;
303
Svet Ganov782043c2017-02-11 00:52:02 +0000304 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700305
306 private final Object mLock = new Object();
307
308 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000309 private IAutoFillManagerClient mServiceClient;
310
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700311 @GuardedBy("mLock")
Koji Fukuiccec6a62017-10-18 17:48:40 +0900312 private Cleaner mServiceClientCleaner;
313
314 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800315 private AutofillCallback mCallback;
316
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700317 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000318
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700319 @GuardedBy("mLock")
320 private int mSessionId = NO_SESSION;
321
322 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700323 private int mState = STATE_UNKNOWN;
324
325 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000326 private boolean mEnabled;
327
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700328 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700329 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700330 @Nullable private ParcelableMap mLastAutofilledData;
331
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700332 /** If view tracking is enabled, contains the tracking state */
333 @GuardedBy("mLock")
334 @Nullable private TrackedViews mTrackedViews;
335
Felipe Leme27e20222017-05-18 15:24:11 -0700336 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
337 @GuardedBy("mLock")
338 @Nullable private ArraySet<AutofillId> mFillableIds;
339
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700340 /** If set, session is commited when the field is clicked. */
341 @GuardedBy("mLock")
342 @Nullable private AutofillId mSaveTriggerId;
343
Dake Gu67decfa2017-12-27 11:48:08 -0800344 /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
345 @GuardedBy("mLock")
346 private boolean mOnInvisibleCalled;
347
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700348 /** If set, session is commited when the activity is finished; otherwise session is canceled. */
349 @GuardedBy("mLock")
350 private boolean mSaveOnFinish;
351
Svet Ganov782043c2017-02-11 00:52:02 +0000352 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800353 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000354 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000355 * Asks the client to start an authentication flow.
356 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700357 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000358 * @param intent The authentication intent.
359 * @param fillInIntent The authentication fill-in intent.
360 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700361 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
362 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800363
364 /**
365 * Tells the client this manager has state to be reset.
366 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700367 void autofillCallbackResetableStateAvailable();
368
369 /**
370 * Request showing the autofill UI.
371 *
372 * @param anchor The real view the UI needs to anchor to.
373 * @param width The width of the fill UI content.
374 * @param height The height of the fill UI content.
375 * @param virtualBounds The bounds of the virtual decendant of the anchor.
376 * @param presenter The presenter that controls the fill UI window.
377 * @return Whether the UI was shown.
378 */
379 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
380 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
381
382 /**
383 * Request hiding the autofill UI.
384 *
385 * @return Whether the UI was hidden.
386 */
387 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700388
389 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700390 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700391 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700392 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700393 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700394 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700395
396 /**
397 * Checks is the client is currently visible as understood by autofill.
398 *
399 * @return {@code true} if the client is currently visible
400 */
401 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700402
403 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800404 * Client might disable enter/exit event e.g. when activity is paused.
405 */
406 boolean isDisablingEnterExitEventForAutofill();
407
408 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700409 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700410 *
Phil Weaver846cda932017-06-15 10:10:06 -0700411 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700412 *
Felipe Leme27e20222017-05-18 15:24:11 -0700413 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700414 */
Phil Weaver846cda932017-06-15 10:10:06 -0700415 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700416
417 /**
418 * Finds a view by traversing the hierarchies of the client.
419 *
Phil Weaver846cda932017-06-15 10:10:06 -0700420 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700421 *
422 * @return The view, or {@code null} if not found
423 */
Phil Weaver846cda932017-06-15 10:10:06 -0700424 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700425
426 /**
427 * Runs the specified action on the UI thread.
428 */
429 void runOnUiThread(Runnable action);
Felipe Leme17292d12017-10-24 14:03:10 -0700430
431 /**
432 * Gets the complete component name of this client.
433 */
434 ComponentName getComponentName();
Svet Ganov782043c2017-02-11 00:52:02 +0000435 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800436
437 /**
438 * @hide
439 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800440 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Leme637e05e2017-12-06 12:09:37 -0800441 mContext = Preconditions.checkNotNull(context, "context cannot be null");
Felipe Leme3461d3c2017-01-19 08:54:55 -0800442 mService = service;
443 }
444
445 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700446 * Restore state after activity lifecycle
447 *
448 * @param savedInstanceState The state to be restored
449 *
450 * {@hide}
451 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700452 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700453 if (!hasAutofillFeature()) {
454 return;
455 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700456 synchronized (mLock) {
457 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
458
Felipe Lemec24a56a2017-08-03 14:27:57 -0700459 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700460 Log.w(TAG, "New session was started before onCreate()");
461 return;
462 }
463
464 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700465 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700466
467 if (mSessionId != NO_SESSION) {
468 ensureServiceClientAddedIfNeededLocked();
469
Felipe Leme637e05e2017-12-06 12:09:37 -0800470 final AutofillClient client = getClient();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700471 if (client != null) {
472 try {
473 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
474 mContext.getActivityToken(), mServiceClient.asBinder());
475
476 if (!sessionWasRestored) {
477 Log.w(TAG, "Session " + mSessionId + " could not be restored");
478 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700479 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700480 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700481 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700482 Log.d(TAG, "session " + mSessionId + " was restored");
483 }
484
485 client.autofillCallbackResetableStateAvailable();
486 }
487 } catch (RemoteException e) {
488 Log.e(TAG, "Could not figure out if there was an autofill session", e);
489 }
490 }
491 }
492 }
493 }
494
495 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700496 * Called once the client becomes visible.
497 *
498 * @see AutofillClient#isVisibleForAutofill()
499 *
500 * {@hide}
501 */
502 public void onVisibleForAutofill() {
503 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700504 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700505 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700506 }
507 }
508 }
509
510 /**
Dake Gu67decfa2017-12-27 11:48:08 -0800511 * Called once the client becomes invisible.
512 *
513 * @see AutofillClient#isVisibleForAutofill()
514 *
515 * {@hide}
516 */
517 public void onInvisibleForAutofill() {
518 synchronized (mLock) {
519 mOnInvisibleCalled = true;
520 }
521 }
522
523 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700524 * Save state before activity lifecycle
525 *
526 * @param outState Place to store the state
527 *
528 * {@hide}
529 */
530 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700531 if (!hasAutofillFeature()) {
532 return;
533 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700534 synchronized (mLock) {
535 if (mSessionId != NO_SESSION) {
536 outState.putInt(SESSION_ID_TAG, mSessionId);
537 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700538 if (mState != STATE_UNKNOWN) {
539 outState.putInt(STATE_TAG, mState);
540 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700541 if (mLastAutofilledData != null) {
542 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
543 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700544 }
545 }
546
547 /**
548 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700549 *
550 * <p>Typically used to determine whether the option to explicitly request autofill should
551 * be offered - see {@link #requestAutofill(View)}.
552 *
553 * @return whether autofill is enabled for the current user.
554 */
555 public boolean isEnabled() {
Felipe Leme3103c632017-12-18 15:05:14 -0800556 if (!hasAutofillFeature()) {
Svet Ganov43574b02017-04-12 09:25:20 -0700557 return false;
558 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700559 synchronized (mLock) {
Felipe Leme3103c632017-12-18 15:05:14 -0800560 if (isDisabledByServiceLocked()) {
561 return false;
562 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700563 ensureServiceClientAddedIfNeededLocked();
564 return mEnabled;
565 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700566 }
567
568 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700569 * Should always be called from {@link AutofillService#getFillEventHistory()}.
570 *
571 * @hide
572 */
573 @Nullable public FillEventHistory getFillEventHistory() {
574 try {
575 return mService.getFillEventHistory();
576 } catch (RemoteException e) {
577 e.rethrowFromSystemServer();
578 return null;
579 }
580 }
581
582 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700583 * Explicitly requests a new autofill context.
584 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700585 * <p>Normally, the autofill context is automatically started if necessary when
586 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
587 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
588 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700589 *
590 * @param view view requesting the new autofill context.
591 */
592 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700593 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700594 }
595
596 /**
597 * Explicitly requests a new autofill context for virtual views.
598 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700599 * <p>Normally, the autofill context is automatically started if necessary when
600 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
601 * cases where it must be explicitly started. For example, when the virtual view offers an
602 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700603 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700604 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
605 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
606 * the absolute bounds could be calculated by:
607 *
608 * <pre class="prettyprint">
609 * int offset[] = new int[2];
610 * getLocationOnScreen(offset);
611 * Rect absBounds = new Rect(bounds.left + offset[0],
612 * bounds.top + offset[1],
613 * bounds.right + offset[0], bounds.bottom + offset[1]);
614 * </pre>
615 *
616 * @param view the virtual view parent.
617 * @param virtualId id identifying the virtual child inside the parent view.
618 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700619 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700620 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
621 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700622 }
623
Felipe Leme2ac463e2017-03-13 14:06:25 -0700624 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700625 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800626 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700627 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800628 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700629 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700630 notifyViewEntered(view, 0);
631 }
632
Felipe Leme17292d12017-10-24 14:03:10 -0700633 private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
Felipe Leme3103c632017-12-18 15:05:14 -0800634 if (isDisabledByServiceLocked()) {
Felipe Leme17292d12017-10-24 14:03:10 -0700635 if (sVerbose) {
636 Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
637 + ") on state " + getStateAsStringLocked());
638 }
639 return true;
640 }
Felipe Leme3103c632017-12-18 15:05:14 -0800641 if (sVerbose && isFinishedLocked()) {
642 Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
643 + ") on state " + getStateAsStringLocked());
Felipe Leme17292d12017-10-24 14:03:10 -0700644 }
645 return false;
646 }
647
Dake Gu67decfa2017-12-27 11:48:08 -0800648 private boolean isClientVisibleForAutofillLocked() {
649 final AutofillClient client = getClient();
650 return client != null && client.isVisibleForAutofill();
651 }
652
653 private boolean isClientDisablingEnterExitEvent() {
654 final AutofillClient client = getClient();
655 return client != null && client.isDisablingEnterExitEventForAutofill();
656 }
657
Felipe Lemed1146422017-04-26 10:17:05 -0700658 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700659 if (!hasAutofillFeature()) {
660 return;
661 }
Dake Gu67decfa2017-12-27 11:48:08 -0800662 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700663 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800664 callback = notifyViewEnteredLocked(view, flags);
665 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700666
Dake Gu67decfa2017-12-27 11:48:08 -0800667 if (callback != null) {
668 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
669 }
670 }
Svet Ganov782043c2017-02-11 00:52:02 +0000671
Dake Gu67decfa2017-12-27 11:48:08 -0800672 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
673 private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
674 if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
675
676 AutofillCallback callback = null;
677
678 ensureServiceClientAddedIfNeededLocked();
679
680 if (!mEnabled) {
681 if (mCallback != null) {
682 callback = mCallback;
683 }
684 } else {
685 // don't notify entered when Activity is already in background
686 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700687 final AutofillId id = getAutofillId(view);
688 final AutofillValue value = view.getAutofillValue();
689
Felipe Lemec24a56a2017-08-03 14:27:57 -0700690 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700691 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700692 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700693 } else {
694 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700695 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700696 }
Felipe Leme24aae152017-03-15 12:33:01 -0700697 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800698 }
Dake Gu67decfa2017-12-27 11:48:08 -0800699 return callback;
Felipe Lemebab851c2017-02-03 18:45:08 -0800700 }
701
702 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700703 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800704 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700705 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800706 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700707 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700708 if (!hasAutofillFeature()) {
709 return;
710 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700711 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800712 notifyViewExitedLocked(view);
713 }
714 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800715
Dake Gu67decfa2017-12-27 11:48:08 -0800716 void notifyViewExitedLocked(@NonNull View view) {
717 ensureServiceClientAddedIfNeededLocked();
718
719 if (mEnabled && isActiveLocked()) {
720 // dont notify exited when Activity is already in background
721 if (!isClientDisablingEnterExitEvent()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700722 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800723
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700724 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700725 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700726 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800727 }
728 }
729
730 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700731 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700732 *
733 * @param view {@link View} that was exited.
734 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700735 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700736 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
737 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
738 }
739
740 /**
741 * Called when a virtual view's visibility changed.
742 *
743 * @param view {@link View} that was exited.
744 * @param virtualId id identifying the virtual child inside the parent view.
745 * @param isVisible visible if the view is visible in the view hierarchy.
746 */
747 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
748 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
749 }
750
751 /**
752 * Called when a view/virtual view's visibility changed.
753 *
754 * @param view {@link View} that was exited.
755 * @param virtualId id identifying the virtual child inside the parent view.
756 * @param isVisible visible if the view is visible in the view hierarchy.
757 * @param virtual Whether the view is virtual.
758 */
759 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
760 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700761 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700762 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700763 final AutofillId id = virtual ? getAutofillId(view, virtualId)
764 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700765 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700766 if (mFillableIds.contains(id)) {
767 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
768 requestHideFillUi(id, view);
769 }
770 }
771 if (mTrackedViews != null) {
Dake Gu67decfa2017-12-27 11:48:08 -0800772 mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700773 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700774 }
775 }
776 }
777
778 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700779 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800780 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700781 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
782 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
783 * the absolute bounds could be calculated by:
784 *
785 * <pre class="prettyprint">
786 * int offset[] = new int[2];
787 * getLocationOnScreen(offset);
788 * Rect absBounds = new Rect(bounds.left + offset[0],
789 * bounds.top + offset[1],
790 * bounds.right + offset[0], bounds.bottom + offset[1]);
791 * </pre>
792 *
793 * @param view the virtual view parent.
794 * @param virtualId id identifying the virtual child inside the parent view.
795 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800796 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700797 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
798 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700799 }
800
Felipe Leme6dcec872017-05-25 11:24:23 -0700801 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700802 if (!hasAutofillFeature()) {
803 return;
804 }
Dake Gu67decfa2017-12-27 11:48:08 -0800805 AutofillCallback callback;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700806 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800807 callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
808 }
Felipe Leme17292d12017-10-24 14:03:10 -0700809
Dake Gu67decfa2017-12-27 11:48:08 -0800810 if (callback != null) {
811 callback.onAutofillEvent(view, virtualId,
812 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
813 }
814 }
Svet Ganov782043c2017-02-11 00:52:02 +0000815
Dake Gu67decfa2017-12-27 11:48:08 -0800816 /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
817 private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
818 int flags) {
819 AutofillCallback callback = null;
820 if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
821
822 ensureServiceClientAddedIfNeededLocked();
823
824 if (!mEnabled) {
825 if (mCallback != null) {
826 callback = mCallback;
827 }
828 } else {
829 // don't notify entered when Activity is already in background
830 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700831 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700832
Felipe Lemec24a56a2017-08-03 14:27:57 -0700833 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700834 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700835 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700836 } else {
837 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700838 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700839 }
Felipe Leme24aae152017-03-15 12:33:01 -0700840 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800841 }
Dake Gu67decfa2017-12-27 11:48:08 -0800842 return callback;
Philip P. Moltmann44611812017-02-23 12:52:46 -0800843 }
844
845 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700846 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800847 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700848 * @param view the virtual view parent.
849 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800850 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700851 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700852 if (!hasAutofillFeature()) {
853 return;
854 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700855 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -0800856 notifyViewExitedLocked(view, virtualId);
857 }
858 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800859
Dake Gu67decfa2017-12-27 11:48:08 -0800860 private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
861 ensureServiceClientAddedIfNeededLocked();
862
863 if (mEnabled && isActiveLocked()) {
864 // don't notify exited when Activity is already in background
865 if (!isClientDisablingEnterExitEvent()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700866 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800867
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700868 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700869 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700870 }
Svet Ganov782043c2017-02-11 00:52:02 +0000871 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800872 }
873
874 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800875 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800876 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800877 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800878 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700879 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700880 if (!hasAutofillFeature()) {
881 return;
882 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700883 AutofillId id = null;
884 boolean valueWasRead = false;
885 AutofillValue value = null;
886
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700887 synchronized (mLock) {
888 // If the session is gone some fields might still be highlighted, hence we have to
889 // remove the isAutofilled property even if no sessions are active.
890 if (mLastAutofilledData == null) {
891 view.setAutofilled(false);
892 } else {
893 id = getAutofillId(view);
894 if (mLastAutofilledData.containsKey(id)) {
895 value = view.getAutofillValue();
896 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700897
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700898 if (Objects.equals(mLastAutofilledData.get(id), value)) {
899 view.setAutofilled(true);
900 } else {
901 view.setAutofilled(false);
902 mLastAutofilledData.remove(id);
903 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700904 } else {
905 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700906 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700907 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700908
Felipe Lemec24a56a2017-08-03 14:27:57 -0700909 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700910 if (sVerbose && mEnabled) {
911 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
912 + getStateAsStringLocked());
913 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700914 return;
915 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800916
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700917 if (id == null) {
918 id = getAutofillId(view);
919 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700920
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700921 if (!valueWasRead) {
922 value = view.getAutofillValue();
923 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700924
Felipe Leme0aa4c502017-04-26 12:36:01 -0700925 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700926 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800927 }
928
Felipe Lemebab851c2017-02-03 18:45:08 -0800929 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700930 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -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.
Felipe Lemebab851c2017-02-03 18:45:08 -0800934 * @param value new value of the child.
935 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700936 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700937 if (!hasAutofillFeature()) {
938 return;
939 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700940 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700941 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700942 return;
943 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800944
Felipe Leme6dcec872017-05-25 11:24:23 -0700945 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700946 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700947 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800948 }
949
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700950
951 /**
952 * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
953 *
954 * @hide
955 */
956 public void notifyViewClicked(View view) {
957 final AutofillId id = view.getAutofillId();
958
959 if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
960
961 synchronized (mLock) {
962 if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
963 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
964 commitLocked();
965 mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
966 mContext.getPackageName());
967 }
968 }
969 }
970
971 /**
972 * Called by {@link android.app.Activity} to commit or cancel the session on finish.
973 *
974 * @hide
975 */
976 public void onActivityFinished() {
977 if (!hasAutofillFeature()) {
978 return;
979 }
980 synchronized (mLock) {
981 if (mSaveOnFinish) {
Felipe Leme601d2202017-11-17 08:21:23 -0800982 if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
Felipe Leme2fe3ade2017-09-28 15:03:36 -0700983 commitLocked();
984 } else {
985 if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
986 cancelLocked();
987 }
988 }
989 }
990
Felipe Lemebab851c2017-02-03 18:45:08 -0800991 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700992 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800993 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700994 * <p>This method is typically called by {@link View Views} that manage virtual views; for
995 * example, when the view is rendering an {@code HTML} page with a form and virtual views
996 * that represent the HTML elements, it should call this method after the form is submitted and
997 * another page is rendered.
998 *
999 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1000 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -08001001 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001002 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -07001003 if (!hasAutofillFeature()) {
1004 return;
1005 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001006 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001007 commitLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001008 }
Felipe Leme0200d9e2017-01-24 15:10:26 -08001009 }
1010
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001011 private void commitLocked() {
1012 if (!mEnabled && !isActiveLocked()) {
1013 return;
1014 }
1015 finishSessionLocked();
1016 }
1017
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001018 /**
1019 * Called to indicate the current autofill context should be cancelled.
1020 *
Felipe Lemeafdfe762017-07-24 11:25:56 -07001021 * <p>This method is typically called by {@link View Views} that manage virtual views; for
1022 * example, when the view is rendering an {@code HTML} page with a form and virtual views
1023 * that represent the HTML elements, it should call this method if the user does not post the
1024 * form but moves to another form in this page.
1025 *
1026 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
1027 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001028 */
1029 public void cancel() {
Felipe Leme601d2202017-11-17 08:21:23 -08001030 if (sVerbose) Log.v(TAG, "cancel() called by app");
Svet Ganov43574b02017-04-12 09:25:20 -07001031 if (!hasAutofillFeature()) {
1032 return;
1033 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001034 synchronized (mLock) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001035 cancelLocked();
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001036 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001037 }
1038
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001039 private void cancelLocked() {
1040 if (!mEnabled && !isActiveLocked()) {
1041 return;
1042 }
1043 cancelSessionLocked();
1044 }
1045
Svet Ganovf965b872017-04-28 16:34:02 -07001046 /** @hide */
1047 public void disableOwnedAutofillServices() {
1048 disableAutofillServices();
1049 }
1050
Svet Ganovf20a0372017-04-10 17:08:05 -07001051 /**
1052 * If the app calling this API has enabled autofill services they
1053 * will be disabled.
1054 */
Svet Ganovf965b872017-04-28 16:34:02 -07001055 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -07001056 if (!hasAutofillFeature()) {
1057 return;
1058 }
Svet Ganovf20a0372017-04-10 17:08:05 -07001059 try {
1060 mService.disableOwnedAutofillServices(mContext.getUserId());
1061 } catch (RemoteException e) {
1062 throw e.rethrowFromSystemServer();
1063 }
1064 }
1065
Felipe Lemedb041182017-04-21 17:33:38 -07001066 /**
1067 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
1068 * enabled for the current user, or {@code false} otherwise.
1069 */
1070 public boolean hasEnabledAutofillServices() {
1071 if (mService == null) return false;
1072
1073 try {
1074 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
1075 } catch (RemoteException e) {
1076 throw e.rethrowFromSystemServer();
1077 }
1078 }
1079
1080 /**
Felipe Leme23c75ff2017-12-14 13:27:44 -08001081 * Returns the component name of the {@link AutofillService} that is enabled for the current
1082 * user.
1083 */
1084 @Nullable
1085 public ComponentName getAutofillServiceComponentName() {
1086 if (mService == null) return null;
1087
1088 try {
1089 return mService.getAutofillServiceComponentName();
1090 } catch (RemoteException e) {
1091 throw e.rethrowFromSystemServer();
1092 }
1093 }
1094
1095 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001096 * Gets the user data used for
1097 * <a href="AutofillService.html#FieldClassification">field classification</a>.
Felipe Leme452886a2017-11-27 13:09:13 -08001098 *
Felipe Leme27f45732017-12-22 09:05:22 -08001099 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1100 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1101 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001102 *
Felipe Leme452886a2017-11-27 13:09:13 -08001103 * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
1104 * reset or if the caller currently does not have an enabled autofill service for the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001105 */
Felipe Leme452886a2017-11-27 13:09:13 -08001106 @Nullable public UserData getUserData() {
1107 try {
1108 return mService.getUserData();
1109 } catch (RemoteException e) {
1110 e.rethrowFromSystemServer();
1111 return null;
1112 }
1113 }
1114
1115 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001116 * Sets the user data used for
1117 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -08001118 *
1119 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1120 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1121 * the user.
Felipe Leme452886a2017-11-27 13:09:13 -08001122 */
Felipe Leme452886a2017-11-27 13:09:13 -08001123 public void setUserData(@Nullable UserData userData) {
1124 try {
1125 mService.setUserData(userData);
1126 } catch (RemoteException e) {
1127 e.rethrowFromSystemServer();
1128 }
1129 }
1130
1131 /**
Felipe Leme78172e72017-12-08 17:01:15 -08001132 * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
1133 * enabled.
1134 *
1135 * <p>As field classification is an expensive operation, it could be disabled, either
1136 * temporarily (for example, because the service exceeded a rate-limit threshold) or
1137 * permanently (for example, because the device is a low-level device).
1138 *
1139 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1140 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1141 * the user.
Felipe Leme329d0402017-12-06 09:22:43 -08001142 */
Felipe Leme329d0402017-12-06 09:22:43 -08001143 public boolean isFieldClassificationEnabled() {
1144 try {
1145 return mService.isFieldClassificationEnabled();
1146 } catch (RemoteException e) {
1147 e.rethrowFromSystemServer();
1148 return false;
1149 }
1150 }
1151
1152 /**
Felipe Leme27f45732017-12-22 09:05:22 -08001153 * Gets the name of the default algorithm used for
1154 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1155 *
1156 * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
1157 * set.
1158 *
1159 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1160 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1161 * the user.
1162 */
1163 @Nullable
1164 public String getDefaultFieldClassificationAlgorithm() {
1165 try {
1166 return mService.getDefaultFieldClassificationAlgorithm();
1167 } catch (RemoteException e) {
1168 e.rethrowFromSystemServer();
1169 return null;
1170 }
1171 }
1172
1173 /**
1174 * Gets the name of all algorithms currently available for
1175 * <a href="AutofillService.html#FieldClassification">field classification</a>.
1176 *
1177 * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
1178 * and it's ignored if the caller currently doesn't have an enabled autofill service for
1179 * the user.
1180 *
1181 * @return list of all algorithms currently available, or an empty list if the caller currently
1182 * does not have an enabled autofill service for the user.
1183 */
1184 @NonNull
1185 public List<String> getAvailableFieldClassificationAlgorithms() {
1186 try {
1187 final List<String> names = mService.getAvailableFieldClassificationAlgorithms();
1188 return names != null ? names : Collections.emptyList();
1189 } catch (RemoteException e) {
1190 e.rethrowFromSystemServer();
1191 return null;
1192 }
1193 }
1194
1195 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -07001196 * Returns {@code true} if autofill is supported by the current device and
1197 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -07001198 *
1199 * <p>Autofill is typically supported, but it could be unsupported in cases like:
1200 * <ol>
1201 * <li>Low-end devices.
1202 * <li>Device policy rules that forbid its usage.
1203 * </ol>
1204 */
1205 public boolean isAutofillSupported() {
1206 if (mService == null) return false;
1207
1208 try {
1209 return mService.isServiceSupported(mContext.getUserId());
1210 } catch (RemoteException e) {
1211 throw e.rethrowFromSystemServer();
1212 }
1213 }
1214
Felipe Leme637e05e2017-12-06 12:09:37 -08001215 // Note: don't need to use locked suffix because mContext is final.
1216 private AutofillClient getClient() {
Felipe Leme686128e2017-10-17 14:02:20 -07001217 final AutofillClient client = mContext.getAutofillClient();
1218 if (client == null && sDebug) {
1219 Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
1220 + mContext);
1221 }
1222 return client;
Svet Ganov782043c2017-02-11 00:52:02 +00001223 }
1224
1225 /** @hide */
Dake Gu67decfa2017-12-27 11:48:08 -08001226 public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
Svet Ganov43574b02017-04-12 09:25:20 -07001227 if (!hasAutofillFeature()) {
1228 return;
1229 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -07001230 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -08001231 // handling the cases where it's not RESULT_OK: it works fine if the service does not
1232 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
1233 // service set the extra and returned RESULT_CANCELED...
1234
Felipe Leme9f9ee252017-04-27 13:56:22 -07001235 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -08001236
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001237 synchronized (mLock) {
Dake Gu67decfa2017-12-27 11:48:08 -08001238 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001239 return;
1240 }
Dake Gu67decfa2017-12-27 11:48:08 -08001241 // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
1242 // of app activity. We enforce enter event to re-show fill ui in such case.
1243 // CTS example:
1244 // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
1245 // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
1246 if (!mOnInvisibleCalled && focusView != null
1247 && focusView.canNotifyAutofillEnterExitEvent()) {
1248 notifyViewExitedLocked(focusView);
1249 notifyViewEnteredLocked(focusView, 0);
1250 }
1251 if (data == null) {
1252 // data is set to null when result is not RESULT_OK
1253 return;
1254 }
1255
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001256 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
1257 final Bundle responseData = new Bundle();
1258 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
Felipe Lemea9372382017-10-09 14:42:36 -07001259 final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
1260 if (newClientState != null) {
1261 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
1262 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001263 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001264 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
1265 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001266 } catch (RemoteException e) {
1267 Log.e(TAG, "Error delivering authentication result", e);
1268 }
Svet Ganov782043c2017-02-11 00:52:02 +00001269 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001270 }
1271
Felipe Leme640f30a2017-03-06 15:44:06 -08001272 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -07001273 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -08001274 }
1275
Felipe Leme6dcec872017-05-25 11:24:23 -07001276 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -07001277 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -08001278 }
1279
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001280 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
1281 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001282 if (sVerbose) {
1283 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -07001284 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -08001285 }
Felipe Leme3103c632017-12-18 15:05:14 -08001286 if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001287 if (sVerbose) {
1288 Log.v(TAG, "not automatically starting session for " + id
Felipe Leme3103c632017-12-18 15:05:14 -08001289 + " on state " + getStateAsStringLocked() + " and flags " + flags);
Felipe Lemec7b45292017-09-19 09:06:20 -07001290 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001291 return;
1292 }
Felipe Lemebab851c2017-02-03 18:45:08 -08001293 try {
Felipe Leme637e05e2017-12-06 12:09:37 -08001294 final AutofillClient client = getClient();
1295 if (client == null) return; // NOTE: getClient() already logd it..
1296
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001297 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -08001298 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001299 mCallback != null, flags, client.getComponentName());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001300 if (mSessionId != NO_SESSION) {
1301 mState = STATE_ACTIVE;
1302 }
Felipe Leme637e05e2017-12-06 12:09:37 -08001303 client.autofillCallbackResetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +00001304 } catch (RemoteException e) {
1305 throw e.rethrowFromSystemServer();
1306 }
1307 }
1308
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001309 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001310 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001311
1312 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001313
Svet Ganov782043c2017-02-11 00:52:02 +00001314 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001315 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001316 } catch (RemoteException e) {
1317 throw e.rethrowFromSystemServer();
1318 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001319
Felipe Lemec24a56a2017-08-03 14:27:57 -07001320 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001321 }
1322
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001323 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001324 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001325
1326 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001327
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001328 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001329 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001330 } catch (RemoteException e) {
1331 throw e.rethrowFromSystemServer();
1332 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001333
Svet Ganov48f10a22017-04-26 18:49:30 -07001334 resetSessionLocked();
1335 }
1336
1337 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001338 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001339 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001340 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001341 mFillableIds = null;
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001342 mSaveTriggerId = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001343 }
1344
Felipe Leme0aa4c502017-04-26 12:36:01 -07001345 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1346 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001347 if (sVerbose && action != ACTION_VIEW_EXITED) {
1348 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1349 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001350 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001351 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1352
Felipe Leme3461d3c2017-01-19 08:54:55 -08001353 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001354 if (restartIfNecessary) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001355 final AutofillClient client = getClient();
1356 if (client == null) return; // NOTE: getClient() already logd it..
1357
Felipe Leme7f33cd32017-05-11 10:10:49 -07001358 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1359 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Felipe Leme17292d12017-10-24 14:03:10 -07001360 mCallback != null, flags, client.getComponentName(), mSessionId, action);
Felipe Leme7f33cd32017-05-11 10:10:49 -07001361 if (newId != mSessionId) {
1362 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1363 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001364 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme637e05e2017-12-06 12:09:37 -08001365 client.autofillCallbackResetableStateAvailable();
Felipe Leme7f33cd32017-05-11 10:10:49 -07001366 }
1367 } else {
1368 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1369 mContext.getUserId());
1370 }
1371
Felipe Leme3461d3c2017-01-19 08:54:55 -08001372 } catch (RemoteException e) {
1373 throw e.rethrowFromSystemServer();
1374 }
1375 }
Svet Ganov782043c2017-02-11 00:52:02 +00001376
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001377 private void ensureServiceClientAddedIfNeededLocked() {
Felipe Leme637e05e2017-12-06 12:09:37 -08001378 if (getClient() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001379 return;
1380 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001381
Svet Ganov782043c2017-02-11 00:52:02 +00001382 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001383 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001384 try {
Koji Fukuiccec6a62017-10-18 17:48:40 +09001385 final int userId = mContext.getUserId();
1386 final int flags = mService.addClient(mServiceClient, userId);
Felipe Leme9f9ee252017-04-27 13:56:22 -07001387 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1388 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1389 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001390 final IAutoFillManager service = mService;
1391 final IAutoFillManagerClient serviceClient = mServiceClient;
1392 mServiceClientCleaner = Cleaner.create(this, () -> {
1393 try {
1394 service.removeClient(serviceClient, userId);
1395 } catch (RemoteException e) {
1396 }
1397 });
Svet Ganov782043c2017-02-11 00:52:02 +00001398 } catch (RemoteException e) {
1399 throw e.rethrowFromSystemServer();
1400 }
1401 }
1402 }
1403
Felipe Lemee6010f22017-03-03 11:19:51 -08001404 /**
1405 * Registers a {@link AutofillCallback} to receive autofill events.
1406 *
1407 * @param callback callback to receive events.
1408 */
1409 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001410 if (!hasAutofillFeature()) {
1411 return;
1412 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001413 synchronized (mLock) {
1414 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001415
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001416 final boolean hadCallback = mCallback != null;
1417 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001418
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001419 if (!hadCallback) {
1420 try {
1421 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1422 } catch (RemoteException e) {
1423 throw e.rethrowFromSystemServer();
1424 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001425 }
1426 }
1427 }
1428
1429 /**
1430 * Unregisters a {@link AutofillCallback} to receive autofill events.
1431 *
1432 * @param callback callback to stop receiving events.
1433 */
1434 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001435 if (!hasAutofillFeature()) {
1436 return;
1437 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001438 synchronized (mLock) {
1439 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001440
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001441 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001442
Felipe Lemee6010f22017-03-03 11:19:51 -08001443 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001444 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001445 } catch (RemoteException e) {
1446 throw e.rethrowFromSystemServer();
1447 }
1448 }
1449 }
1450
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001451 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1452 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1453 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001454 if (anchor == null) {
1455 return;
1456 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001457
1458 AutofillCallback callback = null;
1459 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001460 if (mSessionId == sessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001461 AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001462
1463 if (client != null) {
1464 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1465 anchorBounds, presenter) && mCallback != null) {
1466 callback = mCallback;
1467 }
1468 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001469 }
1470 }
1471
1472 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001473 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001474 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001475 AutofillCallback.EVENT_INPUT_SHOWN);
1476 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001477 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001478 }
1479 }
1480 }
1481
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001482 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1483 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001484 synchronized (mLock) {
1485 if (sessionId == mSessionId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001486 final AutofillClient client = getClient();
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001487 if (client != null) {
Dake Gu67decfa2017-12-27 11:48:08 -08001488 // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
1489 // before onAuthenticationResult()
1490 mOnInvisibleCalled = false;
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001491 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001492 }
1493 }
1494 }
1495 }
1496
Felipe Leme51e29da2017-10-24 14:03:10 -07001497 /** @hide */
1498 public static final int SET_STATE_FLAG_ENABLED = 0x01;
1499 /** @hide */
1500 public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
1501 /** @hide */
1502 public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
1503 /** @hide */
1504 public static final int SET_STATE_FLAG_DEBUG = 0x08;
1505 /** @hide */
1506 public static final int SET_STATE_FLAG_VERBOSE = 0x10;
1507
1508 private void setState(int flags) {
1509 if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001510 synchronized (mLock) {
Felipe Leme51e29da2017-10-24 14:03:10 -07001511 mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
1512 if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001513 // Reset the session state
1514 resetSessionLocked();
1515 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001516 if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001517 // Reset connection to system
1518 mServiceClient = null;
Koji Fukuiccec6a62017-10-18 17:48:40 +09001519 if (mServiceClientCleaner != null) {
1520 mServiceClientCleaner.clean();
1521 mServiceClientCleaner = null;
1522 }
Svet Ganov48f10a22017-04-26 18:49:30 -07001523 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001524 }
Felipe Leme51e29da2017-10-24 14:03:10 -07001525 sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
1526 sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001527 }
1528
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001529 /**
1530 * Sets a view as autofilled if the current value is the {code targetValue}.
1531 *
1532 * @param view The view that is to be autofilled
1533 * @param targetValue The value we want to fill into view
1534 */
1535 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1536 AutofillValue currentValue = view.getAutofillValue();
1537 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001538 synchronized (mLock) {
1539 if (mLastAutofilledData == null) {
1540 mLastAutofilledData = new ParcelableMap(1);
1541 }
1542 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001543 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001544 view.setAutofilled(true);
1545 }
1546 }
1547
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001548 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001549 synchronized (mLock) {
1550 if (sessionId != mSessionId) {
1551 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001552 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001553
Felipe Leme637e05e2017-12-06 12:09:37 -08001554 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001555 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001556 return;
1557 }
1558
1559 final int itemCount = ids.size();
1560 int numApplied = 0;
1561 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001562 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001563
1564 for (int i = 0; i < itemCount; i++) {
1565 final AutofillId id = ids.get(i);
1566 final AutofillValue value = values.get(i);
1567 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001568 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001569 if (view == null) {
1570 Log.w(TAG, "autofill(): no View with id " + viewId);
1571 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001572 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001573 if (id.isVirtual()) {
1574 if (virtualValues == null) {
1575 // Most likely there will be just one view with virtual children.
1576 virtualValues = new ArrayMap<>(1);
1577 }
1578 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1579 if (valuesByParent == null) {
1580 // We don't know the size yet, but usually it will be just a few fields...
1581 valuesByParent = new SparseArray<>(5);
1582 virtualValues.put(view, valuesByParent);
1583 }
1584 valuesByParent.put(id.getVirtualChildId(), value);
1585 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001586 // Mark the view as to be autofilled with 'value'
1587 if (mLastAutofilledData == null) {
1588 mLastAutofilledData = new ParcelableMap(itemCount - i);
1589 }
1590 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001591
1592 view.autofill(value);
1593
1594 // Set as autofilled if the values match now, e.g. when the value was updated
1595 // synchronously.
1596 // If autofill happens async, the view is set to autofilled in
1597 // notifyValueChanged.
1598 setAutofilledIfValuesIs(view, value);
1599
1600 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001601 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001602 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001603
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001604 if (virtualValues != null) {
1605 for (int i = 0; i < virtualValues.size(); i++) {
1606 final View parent = virtualValues.keyAt(i);
1607 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1608 parent.autofill(childrenValues);
1609 numApplied += childrenValues.size();
1610 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001611 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001612
Felipe Lemeb22d6352017-09-08 20:03:53 -07001613 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
1614 .setPackageName(mContext.getPackageName())
1615 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
1616 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001617 mMetricsLogger.write(log);
1618 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001619 }
1620
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001621 /**
1622 * Set the tracked views.
1623 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001624 * @param trackedIds The views to be tracked.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001625 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001626 * @param saveOnFinish Finish the session once the activity is finished.
Felipe Leme27e20222017-05-18 15:24:11 -07001627 * @param fillableIds Views that might anchor FillUI.
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001628 * @param saveTriggerId View that when clicked triggers commit().
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001629 */
Felipe Leme27e20222017-05-18 15:24:11 -07001630 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001631 boolean saveOnAllViewsInvisible, boolean saveOnFinish,
1632 @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001633 synchronized (mLock) {
1634 if (mEnabled && mSessionId == sessionId) {
1635 if (saveOnAllViewsInvisible) {
1636 mTrackedViews = new TrackedViews(trackedIds);
1637 } else {
1638 mTrackedViews = null;
1639 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001640 mSaveOnFinish = saveOnFinish;
Felipe Leme27e20222017-05-18 15:24:11 -07001641 if (fillableIds != null) {
1642 if (mFillableIds == null) {
1643 mFillableIds = new ArraySet<>(fillableIds.length);
1644 }
1645 for (AutofillId id : fillableIds) {
1646 mFillableIds.add(id);
1647 }
1648 if (sVerbose) {
1649 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1650 + ", mFillableIds" + mFillableIds);
1651 }
1652 }
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001653
1654 if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
1655 // Turn off trigger on previous view id.
1656 setNotifyOnClickLocked(mSaveTriggerId, false);
1657 }
1658
1659 if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
1660 // Turn on trigger on new view id.
1661 mSaveTriggerId = saveTriggerId;
1662 setNotifyOnClickLocked(mSaveTriggerId, true);
1663 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001664 }
1665 }
1666 }
1667
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001668 private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
1669 final View view = findView(id);
1670 if (view == null) {
1671 Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
1672 return;
1673 }
1674 view.setNotifyAutofillManagerOnClick(notify);
1675 }
1676
Felipe Lemec24a56a2017-08-03 14:27:57 -07001677 private void setSaveUiState(int sessionId, boolean shown) {
1678 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1679 synchronized (mLock) {
1680 if (mSessionId != NO_SESSION) {
1681 // Race condition: app triggered a new session after the previous session was
1682 // finished but before server called setSaveUiState() - need to cancel the new
1683 // session to avoid further inconsistent behavior.
1684 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1685 + ") called on existing session " + mSessionId + "; cancelling it");
1686 cancelSessionLocked();
1687 }
1688 if (shown) {
1689 mSessionId = sessionId;
1690 mState = STATE_SHOWING_SAVE_UI;
1691 } else {
1692 mSessionId = NO_SESSION;
1693 mState = STATE_UNKNOWN;
1694 }
1695 }
1696 }
1697
Felipe Leme650f7ab2017-09-19 13:08:24 -07001698 /**
1699 * Marks the state of the session as finished.
1700 *
1701 * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
Felipe Leme17292d12017-10-24 14:03:10 -07001702 * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
1703 * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
1704 * requests for the activity).
Felipe Leme650f7ab2017-09-19 13:08:24 -07001705 */
1706 private void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001707 synchronized (mLock) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07001708 if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001709 resetSessionLocked();
Felipe Leme650f7ab2017-09-19 13:08:24 -07001710 mState = newState;
Felipe Lemec7b45292017-09-19 09:06:20 -07001711 }
1712 }
1713
Felipe Leme27e20222017-05-18 15:24:11 -07001714 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001715 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001716 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001717 if (anchor == null) {
1718 return;
1719 }
Felipe Leme27e20222017-05-18 15:24:11 -07001720 requestHideFillUi(id, anchor);
1721 }
1722
1723 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001724
1725 AutofillCallback callback = null;
1726 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001727 // We do not check the session id for two reasons:
1728 // 1. If local and remote session id are off sync the UI would be stuck shown
1729 // 2. There is a race between the user state being destroyed due the fill
1730 // service being uninstalled and the UI being dismissed.
Felipe Leme637e05e2017-12-06 12:09:37 -08001731 AutofillClient client = getClient();
Svet Ganov48f10a22017-04-26 18:49:30 -07001732 if (client != null) {
1733 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1734 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001735 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001736 }
1737 }
1738
1739 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001740 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001741 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001742 AutofillCallback.EVENT_INPUT_HIDDEN);
1743 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001744 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001745 }
1746 }
1747 }
1748
Felipe Leme17292d12017-10-24 14:03:10 -07001749 private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001750 if (sVerbose) {
1751 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
Felipe Leme17292d12017-10-24 14:03:10 -07001752 + ", sessionFinishedState=" + sessionFinishedState);
Felipe Lemec7b45292017-09-19 09:06:20 -07001753 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001754 final View anchor = findView(id);
1755 if (anchor == null) {
1756 return;
1757 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001758
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001759 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001760 synchronized (mLock) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001761 if (mSessionId == sessionId && getClient() != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001762 callback = mCallback;
1763 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001764 }
1765
1766 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001767 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001768 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001769 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1770 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001771 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001772 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001773 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001774
Felipe Leme17292d12017-10-24 14:03:10 -07001775 if (sessionFinishedState != 0) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001776 // Callback call was "hijacked" to also update the session state.
Felipe Leme17292d12017-10-24 14:03:10 -07001777 setSessionFinished(sessionFinishedState);
Felipe Leme4753bb02017-03-22 20:24:00 -07001778 }
1779 }
1780
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001781 /**
1782 * Get an array of viewIds from a List of {@link AutofillId}.
1783 *
1784 * @param autofillIds The autofill ids to convert
1785 *
1786 * @return The array of viewIds.
1787 */
Felipe Leme27e20222017-05-18 15:24:11 -07001788 // TODO: move to Helper as static method
1789 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1790 final int numIds = autofillIds.length;
1791 final int[] viewIds = new int[numIds];
1792 for (int i = 0; i < numIds; i++) {
1793 viewIds[i] = autofillIds[i].getViewId();
1794 }
1795
1796 return viewIds;
1797 }
1798
1799 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001800 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1801 final int numIds = autofillIds.size();
1802 final int[] viewIds = new int[numIds];
1803 for (int i = 0; i < numIds; i++) {
1804 viewIds[i] = autofillIds.get(i).getViewId();
1805 }
1806
1807 return viewIds;
1808 }
1809
1810 /**
1811 * Find a single view by its id.
1812 *
1813 * @param autofillId The autofill id of the view
1814 *
1815 * @return The view or {@code null} if view was not found
1816 */
1817 private View findView(@NonNull AutofillId autofillId) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001818 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001819
1820 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001821 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001822 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001823
Phil Weaver846cda932017-06-15 10:10:06 -07001824 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001825 }
1826
Felipe Lemebb810922017-04-25 15:54:06 -07001827 /** @hide */
1828 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001829 return mService != null;
1830 }
1831
Felipe Lemec24a56a2017-08-03 14:27:57 -07001832 /** @hide */
1833 public void onPendingSaveUi(int operation, IBinder token) {
1834 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1835
1836 synchronized (mLock) {
1837 try {
1838 mService.onPendingSaveUi(operation, token);
1839 } catch (RemoteException e) {
1840 e.rethrowFromSystemServer();
1841 }
1842 }
1843 }
1844
1845 /** @hide */
1846 public void dump(String outerPrefix, PrintWriter pw) {
1847 pw.print(outerPrefix); pw.println("AutofillManager:");
1848 final String pfx = outerPrefix + " ";
1849 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001850 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Leme686128e2017-10-17 14:02:20 -07001851 pw.print(pfx); pw.print("context: "); pw.println(mContext);
Felipe Leme637e05e2017-12-06 12:09:37 -08001852 pw.print(pfx); pw.print("client: "); pw.println(getClient());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001853 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1854 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1855 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
Dake Gu67decfa2017-12-27 11:48:08 -08001856 pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001857 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1858 pw.print(pfx); pw.print("tracked views: ");
1859 if (mTrackedViews == null) {
1860 pw.println("null");
1861 } else {
1862 final String pfx2 = pfx + " ";
1863 pw.println();
1864 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1865 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1866 }
1867 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
Felipe Leme2fe3ade2017-09-28 15:03:36 -07001868 pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
1869 pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
Felipe Leme51e29da2017-10-24 14:03:10 -07001870 pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
1871 pw.print(" verbose: "); pw.println(sVerbose);
Felipe Lemec24a56a2017-08-03 14:27:57 -07001872 }
1873
Felipe Lemec7b45292017-09-19 09:06:20 -07001874 private String getStateAsStringLocked() {
1875 switch (mState) {
1876 case STATE_UNKNOWN:
1877 return "STATE_UNKNOWN";
1878 case STATE_ACTIVE:
1879 return "STATE_ACTIVE";
1880 case STATE_FINISHED:
1881 return "STATE_FINISHED";
1882 case STATE_SHOWING_SAVE_UI:
1883 return "STATE_SHOWING_SAVE_UI";
Felipe Leme17292d12017-10-24 14:03:10 -07001884 case STATE_DISABLED_BY_SERVICE:
1885 return "STATE_DISABLED_BY_SERVICE";
Felipe Lemec7b45292017-09-19 09:06:20 -07001886 default:
1887 return "INVALID:" + mState;
1888 }
1889 }
1890
Felipe Lemec24a56a2017-08-03 14:27:57 -07001891 private boolean isActiveLocked() {
1892 return mState == STATE_ACTIVE;
1893 }
1894
Felipe Leme3103c632017-12-18 15:05:14 -08001895 private boolean isDisabledByServiceLocked() {
Felipe Leme17292d12017-10-24 14:03:10 -07001896 return mState == STATE_DISABLED_BY_SERVICE;
Felipe Lemec7b45292017-09-19 09:06:20 -07001897 }
1898
Felipe Leme3103c632017-12-18 15:05:14 -08001899 private boolean isFinishedLocked() {
1900 return mState == STATE_FINISHED;
1901 }
1902
Felipe Leme9876a6f2017-05-30 15:47:28 -07001903 private void post(Runnable runnable) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001904 final AutofillClient client = getClient();
Felipe Leme9876a6f2017-05-30 15:47:28 -07001905 if (client == null) {
1906 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1907 return;
1908 }
1909 client.runOnUiThread(runnable);
1910 }
1911
Felipe Lemee6010f22017-03-03 11:19:51 -08001912 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001913 * View tracking information. Once all tracked views become invisible the session is finished.
1914 */
1915 private class TrackedViews {
1916 /** Visible tracked views */
1917 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1918
1919 /** Invisible tracked views */
1920 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1921
1922 /**
1923 * Check if set is null or value is in set.
1924 *
1925 * @param set The set or null (== empty set)
1926 * @param value The value that might be in the set
1927 *
1928 * @return {@code true} iff set is not empty and value is in set
1929 */
Felipe Leme27e20222017-05-18 15:24:11 -07001930 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001931 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1932 return set != null && set.contains(value);
1933 }
1934
1935 /**
1936 * Add a value to a set. If set is null, create a new set.
1937 *
1938 * @param set The set or null (== empty set)
1939 * @param valueToAdd The value to add
1940 *
1941 * @return The set including the new value. If set was {@code null}, a set containing only
1942 * the new value.
1943 */
Felipe Leme27e20222017-05-18 15:24:11 -07001944 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001945 @NonNull
1946 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1947 if (set == null) {
1948 set = new ArraySet<>(1);
1949 }
1950
1951 set.add(valueToAdd);
1952
1953 return set;
1954 }
1955
1956 /**
1957 * Remove a value from a set.
1958 *
1959 * @param set The set or null (== empty set)
1960 * @param valueToRemove The value to remove
1961 *
1962 * @return The set without the removed value. {@code null} if set was null, or is empty
1963 * after removal.
1964 */
Felipe Leme27e20222017-05-18 15:24:11 -07001965 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001966 @Nullable
1967 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1968 if (set == null) {
1969 return null;
1970 }
1971
1972 set.remove(valueToRemove);
1973
1974 if (set.isEmpty()) {
1975 return null;
1976 }
1977
1978 return set;
1979 }
1980
1981 /**
1982 * Set the tracked views.
1983 *
1984 * @param trackedIds The views to be tracked
1985 */
Felipe Leme27e20222017-05-18 15:24:11 -07001986 TrackedViews(@Nullable AutofillId[] trackedIds) {
Felipe Leme637e05e2017-12-06 12:09:37 -08001987 final AutofillClient client = getClient();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001988 if (trackedIds != null && client != null) {
1989 final boolean[] isVisible;
1990
1991 if (client.isVisibleForAutofill()) {
1992 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1993 } else {
1994 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001995 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001996 }
1997
Felipe Leme27e20222017-05-18 15:24:11 -07001998 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001999 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07002000 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002001
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002002 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07002003 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002004 } else {
2005 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2006 }
2007 }
2008 }
2009
Felipe Leme9f9ee252017-04-27 13:56:22 -07002010 if (sVerbose) {
2011 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002012 + " mVisibleTrackedIds=" + mVisibleTrackedIds
2013 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
2014 }
2015
2016 if (mVisibleTrackedIds == null) {
2017 finishSessionLocked();
2018 }
2019 }
2020
2021 /**
2022 * Called when a {@link View view's} visibility changes.
2023 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002024 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002025 * @param isVisible visible if the view is visible in the view hierarchy.
2026 */
Dake Gu67decfa2017-12-27 11:48:08 -08002027 void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07002028 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07002029 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002030 + isVisible);
2031 }
2032
Dake Gu67decfa2017-12-27 11:48:08 -08002033 if (isClientVisibleForAutofillLocked()) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002034 if (isVisible) {
2035 if (isInSet(mInvisibleTrackedIds, id)) {
2036 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
2037 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
2038 }
2039 } else {
2040 if (isInSet(mVisibleTrackedIds, id)) {
2041 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
2042 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
2043 }
2044 }
2045 }
2046
2047 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07002048 if (sVerbose) {
2049 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
2050 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002051 finishSessionLocked();
2052 }
2053 }
2054
2055 /**
2056 * Called once the client becomes visible.
2057 *
2058 * @see AutofillClient#isVisibleForAutofill()
2059 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002060 void onVisibleForAutofillLocked() {
2061 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002062 // hence update the visibility state for all views.
Felipe Leme637e05e2017-12-06 12:09:37 -08002063 AutofillClient client = getClient();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002064 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
2065 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
2066 if (client != null) {
2067 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002068 final ArrayList<AutofillId> orderedInvisibleIds =
2069 new ArrayList<>(mInvisibleTrackedIds);
2070 final boolean[] isVisible = client.getViewVisibility(
2071 getViewIds(orderedInvisibleIds));
2072
2073 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
2074 for (int i = 0; i < numInvisibleTrackedIds; i++) {
2075 final AutofillId id = orderedInvisibleIds.get(i);
2076 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002077 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2078
Felipe Leme9f9ee252017-04-27 13:56:22 -07002079 if (sDebug) {
2080 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002081 }
2082 } else {
2083 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2084 }
2085 }
2086 }
2087
2088 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002089 final ArrayList<AutofillId> orderedVisibleIds =
2090 new ArrayList<>(mVisibleTrackedIds);
2091 final boolean[] isVisible = client.getViewVisibility(
2092 getViewIds(orderedVisibleIds));
2093
2094 final int numVisibleTrackedIds = orderedVisibleIds.size();
2095 for (int i = 0; i < numVisibleTrackedIds; i++) {
2096 final AutofillId id = orderedVisibleIds.get(i);
2097
2098 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002099 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
2100 } else {
2101 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
2102
Felipe Leme9f9ee252017-04-27 13:56:22 -07002103 if (sDebug) {
2104 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002105 }
2106 }
2107 }
2108 }
2109
2110 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
2111 mVisibleTrackedIds = updatedVisibleTrackedIds;
2112 }
2113
2114 if (mVisibleTrackedIds == null) {
2115 finishSessionLocked();
2116 }
2117 }
2118 }
2119
2120 /**
Felipe Leme744976e2017-07-31 11:34:14 -07002121 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08002122 *
2123 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002124 * enable / disable such views when the autofill UI is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002125 */
2126 public abstract static class AutofillCallback {
2127
2128 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -07002129 @IntDef(prefix = { "EVENT_INPUT_" }, value = {
2130 EVENT_INPUT_SHOWN,
2131 EVENT_INPUT_HIDDEN,
2132 EVENT_INPUT_UNAVAILABLE
2133 })
Felipe Lemee6010f22017-03-03 11:19:51 -08002134 @Retention(RetentionPolicy.SOURCE)
2135 public @interface AutofillEventType {}
2136
2137 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002138 * The autofill input UI associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08002139 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002140 * <p>If the view provides its own auto-complete UI and its currently shown, it
Felipe Lemee6010f22017-03-03 11:19:51 -08002141 * should be hidden upon receiving this event.
2142 */
2143 public static final int EVENT_INPUT_SHOWN = 1;
2144
2145 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002146 * The autofill input UI associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08002147 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002148 * <p>If the view provides its own auto-complete UI that was hidden upon a
Felipe Lemee6010f22017-03-03 11:19:51 -08002149 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
2150 */
2151 public static final int EVENT_INPUT_HIDDEN = 2;
2152
2153 /**
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002154 * The autofill input UI associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07002155 * autofill is not available.
2156 *
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002157 * <p>If the view provides its own auto-complete UI but was not displaying it
Felipe Leme24aae152017-03-15 12:33:01 -07002158 * to avoid flickering, it could shown it upon receiving this event.
2159 */
2160 public static final int EVENT_INPUT_UNAVAILABLE = 3;
2161
2162 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08002163 * Called after a change in the autofill state associated with a view.
2164 *
2165 * @param view view associated with the change.
2166 *
2167 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2168 */
Felipe Leme81f01d92017-03-16 17:13:25 -07002169 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
2170 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002171
2172 /**
2173 * Called after a change in the autofill state associated with a virtual view.
2174 *
2175 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07002176 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08002177 *
2178 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
2179 */
Felipe Leme6dcec872017-05-25 11:24:23 -07002180 public void onAutofillEvent(@NonNull View view, int virtualId,
2181 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07002182 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002183 }
2184
Felipe Leme640f30a2017-03-06 15:44:06 -08002185 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
2186 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00002187
Felipe Leme640f30a2017-03-06 15:44:06 -08002188 AutofillManagerClient(AutofillManager autofillManager) {
2189 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00002190 }
2191
2192 @Override
Felipe Leme51e29da2017-10-24 14:03:10 -07002193 public void setState(int flags) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002194 final AutofillManager afm = mAfm.get();
2195 if (afm != null) {
Felipe Leme51e29da2017-10-24 14:03:10 -07002196 afm.post(() -> afm.setState(flags));
Svet Ganov782043c2017-02-11 00:52:02 +00002197 }
2198 }
2199
2200 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002201 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002202 final AutofillManager afm = mAfm.get();
2203 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002204 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00002205 }
2206 }
2207
2208 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07002209 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
2210 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002211 final AutofillManager afm = mAfm.get();
2212 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002213 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00002214 }
2215 }
Felipe Lemee6010f22017-03-03 11:19:51 -08002216
2217 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002218 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
2219 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08002220 final AutofillManager afm = mAfm.get();
2221 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002222 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
2223 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07002224 }
2225 }
2226
2227 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07002228 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002229 final AutofillManager afm = mAfm.get();
2230 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002231 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07002232 }
2233 }
2234
2235 @Override
Felipe Leme17292d12017-10-24 14:03:10 -07002236 public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
Felipe Leme4753bb02017-03-22 20:24:00 -07002237 final AutofillManager afm = mAfm.get();
2238 if (afm != null) {
Felipe Leme17292d12017-10-24 14:03:10 -07002239 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
Felipe Lemee6010f22017-03-03 11:19:51 -08002240 }
2241 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07002242
2243 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07002244 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002245 final AutofillManager afm = mAfm.get();
2246 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07002247 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07002248 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07002249 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07002250 } catch (IntentSender.SendIntentException e) {
2251 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
2252 }
2253 });
2254 }
2255 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002256
2257 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07002258 public void setTrackedViews(int sessionId, AutofillId[] ids,
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002259 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
2260 AutofillId saveTriggerId) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002261 final AutofillManager afm = mAfm.get();
2262 if (afm != null) {
Felipe Leme2fe3ade2017-09-28 15:03:36 -07002263 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
2264 saveOnFinish, fillableIds, saveTriggerId));
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07002265 }
2266 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07002267
2268 @Override
2269 public void setSaveUiState(int sessionId, boolean shown) {
2270 final AutofillManager afm = mAfm.get();
2271 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002272 afm.post(() -> afm.setSaveUiState(sessionId, shown));
2273 }
2274 }
2275
2276 @Override
Felipe Leme650f7ab2017-09-19 13:08:24 -07002277 public void setSessionFinished(int newState) {
Felipe Lemec7b45292017-09-19 09:06:20 -07002278 final AutofillManager afm = mAfm.get();
2279 if (afm != null) {
Felipe Leme650f7ab2017-09-19 13:08:24 -07002280 afm.post(() -> afm.setSessionFinished(newState));
Felipe Lemec24a56a2017-08-03 14:27:57 -07002281 }
2282 }
Svet Ganov782043c2017-02-11 00:52:02 +00002283 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08002284}