blob: 39ac39951d23ee9bc4f85fc1e705ca8cca0cdf18 [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;
Felipe Leme3461d3c2017-01-19 08:54:55 -080026import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000027import android.content.Intent;
28import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080029import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070030import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000031import android.os.Bundle;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080033import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070034import android.service.autofill.AutofillService;
35import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070036import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070037import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080038import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070039import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.view.View;
41
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070042import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070043import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.nano.MetricsProto;
45
Felipe Lemee6010f22017-03-03 11:19:51 -080046import java.lang.annotation.Retention;
47import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000048import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070049import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000050import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070051import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000052
Felipe Leme3461d3c2017-01-19 08:54:55 -080053/**
Felipe Leme85d1c2d2017-04-21 08:56:04 -070054 * App entry point to the Autofill Framework.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070055 *
56 * <p>It is safe to call into this from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -080057 */
Felipe Leme640f30a2017-03-06 15:44:06 -080058public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -080059
Felipe Leme640f30a2017-03-06 15:44:06 -080060 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -080061
Svet Ganov782043c2017-02-11 00:52:02 +000062 /**
63 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -070064 *
Svet Ganov782043c2017-02-11 00:52:02 +000065 * <p>
66 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +000067 */
68 public static final String EXTRA_ASSIST_STRUCTURE =
69 "android.view.autofill.extra.ASSIST_STRUCTURE";
70
71 /**
72 * Intent extra: The result of an authentication operation. It is
73 * either a fully populated {@link android.service.autofill.FillResponse}
74 * or a fully populated {@link android.service.autofill.Dataset} if
75 * a response or a dataset is being authenticated respectively.
76 *
77 * <p>
78 * Type: {@link android.service.autofill.FillResponse} or a
79 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +000080 */
81 public static final String EXTRA_AUTHENTICATION_RESULT =
82 "android.view.autofill.extra.AUTHENTICATION_RESULT";
83
Felipe Leme7320ca92017-03-29 15:09:54 -070084 /**
85 * Intent extra: The optional extras provided by the
86 * {@link android.service.autofill.AutofillService}.
87 *
88 * <p>For example, when the service responds to a {@link
89 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
90 * a {@code FillResponse} that requires authentication, the Intent that launches the
91 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -070092 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -070093 *
94 * <p>
95 * Type: {@link android.os.Bundle}
96 */
Felipe Leme37a440fd2017-04-28 10:50:20 -070097 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -060098 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -070099
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700100 static final String SESSION_ID_TAG = "android:sessionId";
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700101 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
102
Felipe Leme0aa4c502017-04-26 12:36:01 -0700103 /** @hide */ public static final int ACTION_START_SESSION = 1;
104 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
105 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
106 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800107
Felipe Leme9f9ee252017-04-27 13:56:22 -0700108
109 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
110 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
111 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
112
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700113 /** Which bits in an authentication id are used for the dataset id */
114 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
115 /** How many bits in an authentication id are used for the dataset id */
116 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
117 /** @hide The index for an undefined data set */
118 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
119
120 /**
121 * Makes an authentication id from a request id and a dataset id.
122 *
123 * @param requestId The request id.
124 * @param datasetId The dataset id.
125 * @return The authentication id.
126 * @hide
127 */
128 public static int makeAuthenticationId(int requestId, int datasetId) {
129 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
130 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
131 }
132
133 /**
134 * Gets the request id from an authentication id.
135 *
136 * @param authRequestId The authentication id.
137 * @return The request id.
138 * @hide
139 */
140 public static int getRequestIdFromAuthenticationId(int authRequestId) {
141 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
142 }
143
144 /**
145 * Gets the dataset id from an authentication id.
146 *
147 * @param authRequestId The authentication id.
148 * @return The dataset id.
149 * @hide
150 */
151 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
152 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
153 }
154
Felipe Leme4753bb02017-03-22 20:24:00 -0700155 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000156
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700157 /**
158 * There is currently no session running.
159 * {@hide}
160 */
161 public static final int NO_SESSION = Integer.MIN_VALUE;
162
Svet Ganov782043c2017-02-11 00:52:02 +0000163 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700164
165 private final Object mLock = new Object();
166
167 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000168 private IAutoFillManagerClient mServiceClient;
169
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700170 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800171 private AutofillCallback mCallback;
172
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700173 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000174
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700175 @GuardedBy("mLock")
176 private int mSessionId = NO_SESSION;
177
178 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000179 private boolean mEnabled;
180
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700181 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700182 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700183 @Nullable private ParcelableMap mLastAutofilledData;
184
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700185 /** If view tracking is enabled, contains the tracking state */
186 @GuardedBy("mLock")
187 @Nullable private TrackedViews mTrackedViews;
188
Svet Ganov782043c2017-02-11 00:52:02 +0000189 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800190 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000191 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000192 * Asks the client to start an authentication flow.
193 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700194 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000195 * @param intent The authentication intent.
196 * @param fillInIntent The authentication fill-in intent.
197 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700198 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
199 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800200
201 /**
202 * Tells the client this manager has state to be reset.
203 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700204 void autofillCallbackResetableStateAvailable();
205
206 /**
207 * Request showing the autofill UI.
208 *
209 * @param anchor The real view the UI needs to anchor to.
210 * @param width The width of the fill UI content.
211 * @param height The height of the fill UI content.
212 * @param virtualBounds The bounds of the virtual decendant of the anchor.
213 * @param presenter The presenter that controls the fill UI window.
214 * @return Whether the UI was shown.
215 */
216 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
217 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
218
219 /**
220 * Request hiding the autofill UI.
221 *
222 * @return Whether the UI was hidden.
223 */
224 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700225
226 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700227 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700228 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700229 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700230 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700231 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700232
233 /**
234 * Checks is the client is currently visible as understood by autofill.
235 *
236 * @return {@code true} if the client is currently visible
237 */
238 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700239
240 /**
241 * Find views by traversing the hierarchies of the client.
242 *
243 * @param viewIds The accessibility ids of the views to find
244 *
245 * @return And array containing the views, or {@code null} if not found
246 */
247 @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
Svet Ganov782043c2017-02-11 00:52:02 +0000248 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800249
250 /**
251 * @hide
252 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800253 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800254 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800255 mService = service;
256 }
257
258 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700259 * Restore state after activity lifecycle
260 *
261 * @param savedInstanceState The state to be restored
262 *
263 * {@hide}
264 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700265 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700266 if (!hasAutofillFeature()) {
267 return;
268 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700269 synchronized (mLock) {
270 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
271
272 if (mSessionId != NO_SESSION) {
273 Log.w(TAG, "New session was started before onCreate()");
274 return;
275 }
276
277 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
278
279 if (mSessionId != NO_SESSION) {
280 ensureServiceClientAddedIfNeededLocked();
281
282 final AutofillClient client = getClientLocked();
283 if (client != null) {
284 try {
285 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
286 mContext.getActivityToken(), mServiceClient.asBinder());
287
288 if (!sessionWasRestored) {
289 Log.w(TAG, "Session " + mSessionId + " could not be restored");
290 mSessionId = NO_SESSION;
291 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700292 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700293 Log.d(TAG, "session " + mSessionId + " was restored");
294 }
295
296 client.autofillCallbackResetableStateAvailable();
297 }
298 } catch (RemoteException e) {
299 Log.e(TAG, "Could not figure out if there was an autofill session", e);
300 }
301 }
302 }
303 }
304 }
305
306 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700307 * Called once the client becomes visible.
308 *
309 * @see AutofillClient#isVisibleForAutofill()
310 *
311 * {@hide}
312 */
313 public void onVisibleForAutofill() {
314 synchronized (mLock) {
315 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700316 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700317 }
318 }
319 }
320
321 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700322 * Save state before activity lifecycle
323 *
324 * @param outState Place to store the state
325 *
326 * {@hide}
327 */
328 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700329 if (!hasAutofillFeature()) {
330 return;
331 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700332 synchronized (mLock) {
333 if (mSessionId != NO_SESSION) {
334 outState.putInt(SESSION_ID_TAG, mSessionId);
335 }
336
337 if (mLastAutofilledData != null) {
338 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
339 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700340 }
341 }
342
343 /**
344 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700345 *
346 * <p>Typically used to determine whether the option to explicitly request autofill should
347 * be offered - see {@link #requestAutofill(View)}.
348 *
349 * @return whether autofill is enabled for the current user.
350 */
351 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700352 if (!hasAutofillFeature()) {
353 return false;
354 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700355 synchronized (mLock) {
356 ensureServiceClientAddedIfNeededLocked();
357 return mEnabled;
358 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700359 }
360
361 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700362 * Should always be called from {@link AutofillService#getFillEventHistory()}.
363 *
364 * @hide
365 */
366 @Nullable public FillEventHistory getFillEventHistory() {
367 try {
368 return mService.getFillEventHistory();
369 } catch (RemoteException e) {
370 e.rethrowFromSystemServer();
371 return null;
372 }
373 }
374
375 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700376 * Explicitly requests a new autofill context.
377 *
378 * <p>Normally, the autofill context is automatically started when autofillable views are
379 * focused, but this method should be used in the cases where it must be explicitly requested,
380 * like a view that provides a contextual menu allowing users to autofill the activity.
381 *
382 * @param view view requesting the new autofill context.
383 */
384 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700385 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700386 }
387
388 /**
389 * Explicitly requests a new autofill context for virtual views.
390 *
391 * <p>Normally, the autofill context is automatically started when autofillable views are
392 * focused, but this method should be used in the cases where it must be explicitly requested,
393 * like a virtual view that provides a contextual menu allowing users to autofill the activity.
394 *
395 * @param view the {@link View} whose descendant is the virtual view.
396 * @param childId id identifying the virtual child inside the view.
397 * @param bounds child boundaries, relative to the top window.
398 */
399 public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
Felipe Lemed1146422017-04-26 10:17:05 -0700400 notifyViewEntered(view, childId, bounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700401 }
402
Felipe Leme2ac463e2017-03-13 14:06:25 -0700403 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700404 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800405 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700406 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800407 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700408 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700409 notifyViewEntered(view, 0);
410 }
411
412 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700413 if (!hasAutofillFeature()) {
414 return;
415 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700416 AutofillCallback callback = null;
417 synchronized (mLock) {
418 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000419
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700420 if (!mEnabled) {
421 if (mCallback != null) {
422 callback = mCallback;
423 }
424 } else {
425 final AutofillId id = getAutofillId(view);
426 final AutofillValue value = view.getAutofillValue();
427
428 if (mSessionId == NO_SESSION) {
429 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700430 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700431 } else {
432 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700433 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700434 }
Felipe Leme24aae152017-03-15 12:33:01 -0700435 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800436 }
437
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700438 if (callback != null) {
439 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000440 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800441 }
442
443 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700444 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800445 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700446 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800447 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700448 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700449 if (!hasAutofillFeature()) {
450 return;
451 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700452 synchronized (mLock) {
453 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800454
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700455 if (mEnabled && mSessionId != NO_SESSION) {
456 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800457
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700458 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700459 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700460 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800461 }
462 }
463
464 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700465 * Called when a {@link View view's} visibility changes.
466 *
467 * @param view {@link View} that was exited.
468 * @param isVisible visible if the view is visible in the view hierarchy.
469 *
470 * @hide
471 */
472 public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
473 synchronized (mLock) {
474 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
475 mTrackedViews.notifyViewVisibilityChange(view, isVisible);
476 }
477 }
478 }
479
480 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700481 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800482 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700483 * @param view the {@link View} whose descendant is the virtual view.
484 * @param childId id identifying the virtual child inside the view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800485 * @param bounds child boundaries, relative to the top window.
Felipe Lemebab851c2017-02-03 18:45:08 -0800486 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700487 public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
Felipe Lemed1146422017-04-26 10:17:05 -0700488 notifyViewEntered(view, childId, bounds, 0);
489 }
490
491 private void notifyViewEntered(View view, int childId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700492 if (!hasAutofillFeature()) {
493 return;
494 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700495 AutofillCallback callback = null;
496 synchronized (mLock) {
497 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000498
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700499 if (!mEnabled) {
500 if (mCallback != null) {
501 callback = mCallback;
502 }
503 } else {
504 final AutofillId id = getAutofillId(view, childId);
505
506 if (mSessionId == NO_SESSION) {
507 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700508 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700509 } else {
510 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700511 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700512 }
Felipe Leme24aae152017-03-15 12:33:01 -0700513 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800514 }
515
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700516 if (callback != null) {
517 callback.onAutofillEvent(view, childId,
518 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800519 }
520 }
521
522 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700523 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800524 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700525 * @param view the {@link View} whose descendant is the virtual view.
526 * @param childId id identifying the virtual child inside the view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800527 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700528 public void notifyViewExited(@NonNull View view, int childId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700529 if (!hasAutofillFeature()) {
530 return;
531 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700532 synchronized (mLock) {
533 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800534
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700535 if (mEnabled && mSessionId != NO_SESSION) {
536 final AutofillId id = getAutofillId(view, childId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800537
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700538 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700539 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700540 }
Svet Ganov782043c2017-02-11 00:52:02 +0000541 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800542 }
543
544 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800545 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800546 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800547 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800548 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700549 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700550 if (!hasAutofillFeature()) {
551 return;
552 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700553 AutofillId id = null;
554 boolean valueWasRead = false;
555 AutofillValue value = null;
556
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700557 synchronized (mLock) {
558 // If the session is gone some fields might still be highlighted, hence we have to
559 // remove the isAutofilled property even if no sessions are active.
560 if (mLastAutofilledData == null) {
561 view.setAutofilled(false);
562 } else {
563 id = getAutofillId(view);
564 if (mLastAutofilledData.containsKey(id)) {
565 value = view.getAutofillValue();
566 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700567
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700568 if (Objects.equals(mLastAutofilledData.get(id), value)) {
569 view.setAutofilled(true);
570 } else {
571 view.setAutofilled(false);
572 mLastAutofilledData.remove(id);
573 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700574 } else {
575 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700576 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700577 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700578
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700579 if (!mEnabled || mSessionId == NO_SESSION) {
580 return;
581 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800582
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700583 if (id == null) {
584 id = getAutofillId(view);
585 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700586
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700587 if (!valueWasRead) {
588 value = view.getAutofillValue();
589 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700590
Felipe Leme0aa4c502017-04-26 12:36:01 -0700591 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700592 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800593 }
594
Felipe Lemebab851c2017-02-03 18:45:08 -0800595 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800596 * Called to indicate the value of an autofillable virtual {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800597 *
Felipe Leme2ac463e2017-03-13 14:06:25 -0700598 * @param view the {@link View} whose descendant is the virtual view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800599 * @param childId id identifying the virtual child inside the parent view.
600 * @param value new value of the child.
601 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700602 public void notifyValueChanged(View view, int childId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700603 if (!hasAutofillFeature()) {
604 return;
605 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700606 synchronized (mLock) {
607 if (!mEnabled || mSessionId == NO_SESSION) {
608 return;
609 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800610
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700611 final AutofillId id = getAutofillId(view, childId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700612 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700613 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800614 }
615
616 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700617 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800618 *
619 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
620 * call this method after the form is submitted and another page is rendered.
621 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700622 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700623 if (!hasAutofillFeature()) {
624 return;
625 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700626 synchronized (mLock) {
627 if (!mEnabled && mSessionId == NO_SESSION) {
628 return;
629 }
Svet Ganov782043c2017-02-11 00:52:02 +0000630
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700631 finishSessionLocked();
632 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800633 }
634
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700635 /**
636 * Called to indicate the current autofill context should be cancelled.
637 *
638 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
639 * call this method if the user does not post the form but moves to another form in this page.
640 */
641 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700642 if (!hasAutofillFeature()) {
643 return;
644 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700645 synchronized (mLock) {
646 if (!mEnabled && mSessionId == NO_SESSION) {
647 return;
648 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700649
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700650 cancelSessionLocked();
651 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700652 }
653
Svet Ganovf965b872017-04-28 16:34:02 -0700654 /** @hide */
655 public void disableOwnedAutofillServices() {
656 disableAutofillServices();
657 }
658
Svet Ganovf20a0372017-04-10 17:08:05 -0700659 /**
660 * If the app calling this API has enabled autofill services they
661 * will be disabled.
662 */
Svet Ganovf965b872017-04-28 16:34:02 -0700663 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700664 if (!hasAutofillFeature()) {
665 return;
666 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700667 try {
668 mService.disableOwnedAutofillServices(mContext.getUserId());
669 } catch (RemoteException e) {
670 throw e.rethrowFromSystemServer();
671 }
672 }
673
Felipe Lemedb041182017-04-21 17:33:38 -0700674 /**
675 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
676 * enabled for the current user, or {@code false} otherwise.
677 */
678 public boolean hasEnabledAutofillServices() {
679 if (mService == null) return false;
680
681 try {
682 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
683 } catch (RemoteException e) {
684 throw e.rethrowFromSystemServer();
685 }
686 }
687
688 /**
689 * Returns {@code true} if Autofill is supported for this user.
690 *
691 * <p>Autofill is typically supported, but it could be unsupported in cases like:
692 * <ol>
693 * <li>Low-end devices.
694 * <li>Device policy rules that forbid its usage.
695 * </ol>
696 */
697 public boolean isAutofillSupported() {
698 if (mService == null) return false;
699
700 try {
701 return mService.isServiceSupported(mContext.getUserId());
702 } catch (RemoteException e) {
703 throw e.rethrowFromSystemServer();
704 }
705 }
706
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700707 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800708 if (mContext instanceof AutofillClient) {
709 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000710 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800711 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000712 }
713
714 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700715 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700716 if (!hasAutofillFeature()) {
717 return;
718 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700719 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800720 // handling the cases where it's not RESULT_OK: it works fine if the service does not
721 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
722 // service set the extra and returned RESULT_CANCELED...
723
Felipe Leme9f9ee252017-04-27 13:56:22 -0700724 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800725
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700726 synchronized (mLock) {
727 if (mSessionId == NO_SESSION || data == null) {
728 return;
729 }
730 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
731 final Bundle responseData = new Bundle();
732 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
733 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700734 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
735 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700736 } catch (RemoteException e) {
737 Log.e(TAG, "Error delivering authentication result", e);
738 }
Svet Ganov782043c2017-02-11 00:52:02 +0000739 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800740 }
741
Felipe Leme640f30a2017-03-06 15:44:06 -0800742 private static AutofillId getAutofillId(View view) {
743 return new AutofillId(view.getAccessibilityViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800744 }
745
Felipe Leme640f30a2017-03-06 15:44:06 -0800746 private static AutofillId getAutofillId(View parent, int childId) {
747 return new AutofillId(parent.getAccessibilityViewId(), childId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800748 }
749
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700750 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
751 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700752 if (sVerbose) {
753 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Leme2ac463e2017-03-13 14:06:25 -0700754 + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800755 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800756
Felipe Lemebab851c2017-02-03 18:45:08 -0800757 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700758 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -0800759 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800760 mCallback != null, flags, mContext.getOpPackageName());
Felipe Leme0aa4c502017-04-26 12:36:01 -0700761 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800762 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700763 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800764 }
Svet Ganov782043c2017-02-11 00:52:02 +0000765 } catch (RemoteException e) {
766 throw e.rethrowFromSystemServer();
767 }
768 }
769
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700770 private void finishSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700771 if (sVerbose) Log.v(TAG, "finishSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700772
Svet Ganov782043c2017-02-11 00:52:02 +0000773 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700774 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800775 } catch (RemoteException e) {
776 throw e.rethrowFromSystemServer();
777 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700778
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700779 mTrackedViews = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 mSessionId = NO_SESSION;
Felipe Lemebab851c2017-02-03 18:45:08 -0800781 }
782
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700783 private void cancelSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700784 if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700785
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700786 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700787 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700788 } catch (RemoteException e) {
789 throw e.rethrowFromSystemServer();
790 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700791
Svet Ganov48f10a22017-04-26 18:49:30 -0700792 resetSessionLocked();
793 }
794
795 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700796 mSessionId = NO_SESSION;
Svet Ganov48f10a22017-04-26 18:49:30 -0700797 mTrackedViews = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700798 }
799
Felipe Leme0aa4c502017-04-26 12:36:01 -0700800 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
801 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700802 if (sVerbose && action != ACTION_VIEW_EXITED) {
803 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
804 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800805 }
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800806
Felipe Leme7f33cd32017-05-11 10:10:49 -0700807 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
808
Felipe Leme3461d3c2017-01-19 08:54:55 -0800809 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -0700810 if (restartIfNecessary) {
811 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
812 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
813 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
814 if (newId != mSessionId) {
815 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
816 mSessionId = newId;
817 final AutofillClient client = getClientLocked();
818 if (client != null) {
819 client.autofillCallbackResetableStateAvailable();
820 }
821 }
822 } else {
823 mService.updateSession(mSessionId, id, bounds, value, action, flags,
824 mContext.getUserId());
825 }
826
Felipe Leme3461d3c2017-01-19 08:54:55 -0800827 } catch (RemoteException e) {
828 throw e.rethrowFromSystemServer();
829 }
830 }
Svet Ganov782043c2017-02-11 00:52:02 +0000831
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700832 private void ensureServiceClientAddedIfNeededLocked() {
833 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +0000834 return;
835 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700836
Svet Ganov782043c2017-02-11 00:52:02 +0000837 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800838 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +0000839 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700840 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
841 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
842 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
843 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +0000844 } catch (RemoteException e) {
845 throw e.rethrowFromSystemServer();
846 }
847 }
848 }
849
Felipe Lemee6010f22017-03-03 11:19:51 -0800850 /**
851 * Registers a {@link AutofillCallback} to receive autofill events.
852 *
853 * @param callback callback to receive events.
854 */
855 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700856 if (!hasAutofillFeature()) {
857 return;
858 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700859 synchronized (mLock) {
860 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800861
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700862 final boolean hadCallback = mCallback != null;
863 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -0800864
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700865 if (!hadCallback) {
866 try {
867 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
868 } catch (RemoteException e) {
869 throw e.rethrowFromSystemServer();
870 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800871 }
872 }
873 }
874
875 /**
876 * Unregisters a {@link AutofillCallback} to receive autofill events.
877 *
878 * @param callback callback to stop receiving events.
879 */
880 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700881 if (!hasAutofillFeature()) {
882 return;
883 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700884 synchronized (mLock) {
885 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800886
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700887 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800888
Felipe Lemee6010f22017-03-03 11:19:51 -0800889 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700890 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -0800891 } catch (RemoteException e) {
892 throw e.rethrowFromSystemServer();
893 }
894 }
895 }
896
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700897 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
898 Rect anchorBounds, IAutofillWindowPresenter presenter) {
899 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -0700900 if (anchor == null) {
901 return;
902 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700903
904 AutofillCallback callback = null;
905 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700906 if (mSessionId == sessionId) {
907 AutofillClient client = getClientLocked();
908
909 if (client != null) {
910 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
911 anchorBounds, presenter) && mCallback != null) {
912 callback = mCallback;
913 }
914 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700915 }
916 }
917
918 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700919 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700920 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700921 AutofillCallback.EVENT_INPUT_SHOWN);
922 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700923 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -0700924 }
925 }
926 }
927
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700928 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
929 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700930 synchronized (mLock) {
931 if (sessionId == mSessionId) {
932 AutofillClient client = getClientLocked();
933 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700934 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700935 }
936 }
937 }
938 }
939
Svet Ganov48f10a22017-04-26 18:49:30 -0700940 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700941 synchronized (mLock) {
942 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -0700943 if (!mEnabled || resetSession) {
944 // Reset the session state
945 resetSessionLocked();
946 }
947 if (resetClient) {
948 // Reset connection to system
949 mServiceClient = null;
950 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700951 }
952 }
953
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700954 /**
955 * Sets a view as autofilled if the current value is the {code targetValue}.
956 *
957 * @param view The view that is to be autofilled
958 * @param targetValue The value we want to fill into view
959 */
960 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
961 AutofillValue currentValue = view.getAutofillValue();
962 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700963 synchronized (mLock) {
964 if (mLastAutofilledData == null) {
965 mLastAutofilledData = new ParcelableMap(1);
966 }
967 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700968 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700969 view.setAutofilled(true);
970 }
971 }
972
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700973 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700974 synchronized (mLock) {
975 if (sessionId != mSessionId) {
976 return;
Felipe Leme4753bb02017-03-22 20:24:00 -0700977 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700978
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700979 final AutofillClient client = getClientLocked();
980 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700981 return;
982 }
983
984 final int itemCount = ids.size();
985 int numApplied = 0;
986 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700987 final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700988
989 for (int i = 0; i < itemCount; i++) {
990 final AutofillId id = ids.get(i);
991 final AutofillValue value = values.get(i);
992 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700993 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700994 if (view == null) {
995 Log.w(TAG, "autofill(): no View with id " + viewId);
996 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -0700997 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700998 if (id.isVirtual()) {
999 if (virtualValues == null) {
1000 // Most likely there will be just one view with virtual children.
1001 virtualValues = new ArrayMap<>(1);
1002 }
1003 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1004 if (valuesByParent == null) {
1005 // We don't know the size yet, but usually it will be just a few fields...
1006 valuesByParent = new SparseArray<>(5);
1007 virtualValues.put(view, valuesByParent);
1008 }
1009 valuesByParent.put(id.getVirtualChildId(), value);
1010 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001011 // Mark the view as to be autofilled with 'value'
1012 if (mLastAutofilledData == null) {
1013 mLastAutofilledData = new ParcelableMap(itemCount - i);
1014 }
1015 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001016
1017 view.autofill(value);
1018
1019 // Set as autofilled if the values match now, e.g. when the value was updated
1020 // synchronously.
1021 // If autofill happens async, the view is set to autofilled in
1022 // notifyValueChanged.
1023 setAutofilledIfValuesIs(view, value);
1024
1025 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001026 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001027 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001028
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001029 if (virtualValues != null) {
1030 for (int i = 0; i < virtualValues.size(); i++) {
1031 final View parent = virtualValues.keyAt(i);
1032 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1033 parent.autofill(childrenValues);
1034 numApplied += childrenValues.size();
1035 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001036 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001037
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001038 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1039 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1040 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1041 numApplied);
1042 mMetricsLogger.write(log);
1043 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001044 }
1045
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001046 /**
1047 * Set the tracked views.
1048 *
1049 * @param trackedIds The views to be tracked
1050 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
1051 */
1052 private void setTrackedViews(int sessionId, List<AutofillId> trackedIds,
1053 boolean saveOnAllViewsInvisible) {
1054 synchronized (mLock) {
1055 if (mEnabled && mSessionId == sessionId) {
1056 if (saveOnAllViewsInvisible) {
1057 mTrackedViews = new TrackedViews(trackedIds);
1058 } else {
1059 mTrackedViews = null;
1060 }
1061 }
1062 }
1063 }
1064
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001065 private void requestHideFillUi(int sessionId, AutofillId id) {
1066 final View anchor = findView(id);
1067 if (anchor == null) {
1068 return;
1069 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001070
1071 AutofillCallback callback = null;
1072 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001073 // We do not check the session id for two reasons:
1074 // 1. If local and remote session id are off sync the UI would be stuck shown
1075 // 2. There is a race between the user state being destroyed due the fill
1076 // service being uninstalled and the UI being dismissed.
1077 AutofillClient client = getClientLocked();
1078 if (client != null) {
1079 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1080 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001081 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001082 }
1083 }
1084
1085 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001086 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001087 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001088 AutofillCallback.EVENT_INPUT_HIDDEN);
1089 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001090 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001091 }
1092 }
1093 }
1094
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001095 private void notifyNoFillUi(int sessionId, AutofillId id) {
1096 final View anchor = findView(id);
1097 if (anchor == null) {
1098 return;
1099 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001100
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001101 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001102 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001103 if (mSessionId == sessionId && getClientLocked() != null) {
1104 callback = mCallback;
1105 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001106 }
1107
1108 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001109 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001110 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001111 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1112 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001113 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001114 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001115
Felipe Leme4753bb02017-03-22 20:24:00 -07001116 }
1117 }
1118
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001119 /**
1120 * Get an array of viewIds from a List of {@link AutofillId}.
1121 *
1122 * @param autofillIds The autofill ids to convert
1123 *
1124 * @return The array of viewIds.
1125 */
1126 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1127 final int numIds = autofillIds.size();
1128 final int[] viewIds = new int[numIds];
1129 for (int i = 0; i < numIds; i++) {
1130 viewIds[i] = autofillIds.get(i).getViewId();
1131 }
1132
1133 return viewIds;
1134 }
1135
1136 /**
1137 * Find a single view by its id.
1138 *
1139 * @param autofillId The autofill id of the view
1140 *
1141 * @return The view or {@code null} if view was not found
1142 */
1143 private View findView(@NonNull AutofillId autofillId) {
1144 final AutofillClient client = getClientLocked();
1145
1146 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001147 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001148 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001149
1150 return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
Felipe Lemee6010f22017-03-03 11:19:51 -08001151 }
1152
Felipe Lemebb810922017-04-25 15:54:06 -07001153 /** @hide */
1154 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001155 return mService != null;
1156 }
1157
Felipe Lemee6010f22017-03-03 11:19:51 -08001158 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001159 * View tracking information. Once all tracked views become invisible the session is finished.
1160 */
1161 private class TrackedViews {
1162 /** Visible tracked views */
1163 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1164
1165 /** Invisible tracked views */
1166 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1167
1168 /**
1169 * Check if set is null or value is in set.
1170 *
1171 * @param set The set or null (== empty set)
1172 * @param value The value that might be in the set
1173 *
1174 * @return {@code true} iff set is not empty and value is in set
1175 */
1176 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1177 return set != null && set.contains(value);
1178 }
1179
1180 /**
1181 * Add a value to a set. If set is null, create a new set.
1182 *
1183 * @param set The set or null (== empty set)
1184 * @param valueToAdd The value to add
1185 *
1186 * @return The set including the new value. If set was {@code null}, a set containing only
1187 * the new value.
1188 */
1189 @NonNull
1190 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1191 if (set == null) {
1192 set = new ArraySet<>(1);
1193 }
1194
1195 set.add(valueToAdd);
1196
1197 return set;
1198 }
1199
1200 /**
1201 * Remove a value from a set.
1202 *
1203 * @param set The set or null (== empty set)
1204 * @param valueToRemove The value to remove
1205 *
1206 * @return The set without the removed value. {@code null} if set was null, or is empty
1207 * after removal.
1208 */
1209 @Nullable
1210 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1211 if (set == null) {
1212 return null;
1213 }
1214
1215 set.remove(valueToRemove);
1216
1217 if (set.isEmpty()) {
1218 return null;
1219 }
1220
1221 return set;
1222 }
1223
1224 /**
1225 * Set the tracked views.
1226 *
1227 * @param trackedIds The views to be tracked
1228 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001229 TrackedViews(List<AutofillId> trackedIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001230 mVisibleTrackedIds = null;
1231 mInvisibleTrackedIds = null;
1232
1233 AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001234 if (trackedIds != null && client != null) {
1235 final boolean[] isVisible;
1236
1237 if (client.isVisibleForAutofill()) {
1238 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1239 } else {
1240 // All false
1241 isVisible = new boolean[trackedIds.size()];
1242 }
1243
1244 final int numIds = trackedIds.size();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001245 for (int i = 0; i < numIds; i++) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001246 final AutofillId id = trackedIds.get(i);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001247
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001248 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001249 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001250 } else {
1251 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1252 }
1253 }
1254 }
1255
Felipe Leme9f9ee252017-04-27 13:56:22 -07001256 if (sVerbose) {
1257 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001258 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1259 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1260 }
1261
1262 if (mVisibleTrackedIds == null) {
1263 finishSessionLocked();
1264 }
1265 }
1266
1267 /**
1268 * Called when a {@link View view's} visibility changes.
1269 *
1270 * @param view {@link View} that was exited.
1271 * @param isVisible visible if the view is visible in the view hierarchy.
1272 */
1273 void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
1274 AutofillId id = getAutofillId(view);
1275 AutofillClient client = getClientLocked();
1276
Felipe Leme9f9ee252017-04-27 13:56:22 -07001277 if (sDebug) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001278 Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible="
1279 + isVisible);
1280 }
1281
1282 if (client != null && client.isVisibleForAutofill()) {
1283 if (isVisible) {
1284 if (isInSet(mInvisibleTrackedIds, id)) {
1285 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1286 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1287 }
1288 } else {
1289 if (isInSet(mVisibleTrackedIds, id)) {
1290 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1291 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1292 }
1293 }
1294 }
1295
1296 if (mVisibleTrackedIds == null) {
1297 finishSessionLocked();
1298 }
1299 }
1300
1301 /**
1302 * Called once the client becomes visible.
1303 *
1304 * @see AutofillClient#isVisibleForAutofill()
1305 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001306 void onVisibleForAutofillLocked() {
1307 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001308 // hence update the visibility state for all views.
1309 AutofillClient client = getClientLocked();
1310 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1311 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1312 if (client != null) {
1313 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001314 final ArrayList<AutofillId> orderedInvisibleIds =
1315 new ArrayList<>(mInvisibleTrackedIds);
1316 final boolean[] isVisible = client.getViewVisibility(
1317 getViewIds(orderedInvisibleIds));
1318
1319 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1320 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1321 final AutofillId id = orderedInvisibleIds.get(i);
1322 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001323 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1324
Felipe Leme9f9ee252017-04-27 13:56:22 -07001325 if (sDebug) {
1326 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001327 }
1328 } else {
1329 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1330 }
1331 }
1332 }
1333
1334 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001335 final ArrayList<AutofillId> orderedVisibleIds =
1336 new ArrayList<>(mVisibleTrackedIds);
1337 final boolean[] isVisible = client.getViewVisibility(
1338 getViewIds(orderedVisibleIds));
1339
1340 final int numVisibleTrackedIds = orderedVisibleIds.size();
1341 for (int i = 0; i < numVisibleTrackedIds; i++) {
1342 final AutofillId id = orderedVisibleIds.get(i);
1343
1344 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001345 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1346 } else {
1347 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1348
Felipe Leme9f9ee252017-04-27 13:56:22 -07001349 if (sDebug) {
1350 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001351 }
1352 }
1353 }
1354 }
1355
1356 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1357 mVisibleTrackedIds = updatedVisibleTrackedIds;
1358 }
1359
1360 if (mVisibleTrackedIds == null) {
1361 finishSessionLocked();
1362 }
1363 }
1364 }
1365
1366 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001367 * Callback for auto-fill related events.
1368 *
1369 * <p>Typically used for applications that display their own "auto-complete" views, so they can
1370 * enable / disable such views when the auto-fill UI affordance is shown / hidden.
1371 */
1372 public abstract static class AutofillCallback {
1373
1374 /** @hide */
1375 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1376 @Retention(RetentionPolicy.SOURCE)
1377 public @interface AutofillEventType {}
1378
1379 /**
1380 * The auto-fill input UI affordance associated with the view was shown.
1381 *
1382 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1383 * should be hidden upon receiving this event.
1384 */
1385 public static final int EVENT_INPUT_SHOWN = 1;
1386
1387 /**
1388 * The auto-fill input UI affordance associated with the view was hidden.
1389 *
1390 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1391 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1392 */
1393 public static final int EVENT_INPUT_HIDDEN = 2;
1394
1395 /**
Felipe Leme24aae152017-03-15 12:33:01 -07001396 * The auto-fill input UI affordance associated with the view won't be shown because
1397 * autofill is not available.
1398 *
1399 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1400 * to avoid flickering, it could shown it upon receiving this event.
1401 */
1402 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1403
1404 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001405 * Called after a change in the autofill state associated with a view.
1406 *
1407 * @param view view associated with the change.
1408 *
1409 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1410 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001411 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1412 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001413
1414 /**
1415 * Called after a change in the autofill state associated with a virtual view.
1416 *
1417 * @param view parent view associated with the change.
1418 * @param childId id identifying the virtual child inside the parent view.
1419 *
1420 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1421 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001422 public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
1423 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001424 }
1425
Felipe Leme640f30a2017-03-06 15:44:06 -08001426 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1427 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001428
Felipe Leme640f30a2017-03-06 15:44:06 -08001429 AutofillManagerClient(AutofillManager autofillManager) {
1430 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001431 }
1432
1433 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001434 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001435 final AutofillManager afm = mAfm.get();
1436 if (afm != null) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001437 afm.mContext.getMainThreadHandler().post(
1438 () -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001439 }
1440 }
1441
1442 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001443 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001444 final AutofillManager afm = mAfm.get();
1445 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001446 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001447 () -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001448 }
1449 }
1450
1451 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001452 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1453 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001454 final AutofillManager afm = mAfm.get();
1455 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001456 afm.mContext.getMainThreadHandler().post(
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001457 () -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001458 }
1459 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001460
1461 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001462 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1463 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001464 final AutofillManager afm = mAfm.get();
1465 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001466 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001467 () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1468 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001469 }
1470 }
1471
1472 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001473 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001474 final AutofillManager afm = mAfm.get();
1475 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001476 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001477 () -> afm.requestHideFillUi(sessionId, id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001478 }
1479 }
1480
1481 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001482 public void notifyNoFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001483 final AutofillManager afm = mAfm.get();
1484 if (afm != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001485 afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id));
Felipe Lemee6010f22017-03-03 11:19:51 -08001486 }
1487 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001488
1489 @Override
1490 public void startIntentSender(IntentSender intentSender) {
1491 final AutofillManager afm = mAfm.get();
1492 if (afm != null) {
1493 afm.mContext.getMainThreadHandler().post(() -> {
1494 try {
1495 afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
1496 } catch (IntentSender.SendIntentException e) {
1497 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1498 }
1499 });
1500 }
1501 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001502
1503 @Override
1504 public void setTrackedViews(int sessionId, List<AutofillId> ids,
1505 boolean saveOnAllViewsInvisible) {
1506 final AutofillManager afm = mAfm.get();
1507 if (afm != null) {
1508 afm.mContext.getMainThreadHandler().post(
1509 () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible)
1510 );
1511 }
1512 }
Svet Ganov782043c2017-02-11 00:52:02 +00001513 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001514}