blob: a9299bf65680ca98e9dd3ca88a18ca796594744e [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 Leme3461d3c2017-01-19 08:54:55 -080027import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000028import android.content.Intent;
29import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080030import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070031import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Bundle;
Svet Ganov782043c2017-02-11 00:52:02 +000033import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080034import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070035import android.service.autofill.AutofillService;
36import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070037import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070038import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080039import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070040import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080041import android.view.View;
42
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070043import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070044import com.android.internal.logging.MetricsLogger;
45import com.android.internal.logging.nano.MetricsProto;
46
Felipe Lemee6010f22017-03-03 11:19:51 -080047import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000049import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070050import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000051import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070052import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000053
Felipe Leme3461d3c2017-01-19 08:54:55 -080054/**
Felipe Leme85d1c2d2017-04-21 08:56:04 -070055 * App entry point to the Autofill Framework.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070056 *
57 * <p>It is safe to call into this from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -080058 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060059@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -080060public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -080061
Felipe Leme640f30a2017-03-06 15:44:06 -080062 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -080063
Svet Ganov782043c2017-02-11 00:52:02 +000064 /**
65 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -070066 *
Svet Ganov782043c2017-02-11 00:52:02 +000067 * <p>
68 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +000069 */
70 public static final String EXTRA_ASSIST_STRUCTURE =
71 "android.view.autofill.extra.ASSIST_STRUCTURE";
72
73 /**
74 * Intent extra: The result of an authentication operation. It is
75 * either a fully populated {@link android.service.autofill.FillResponse}
76 * or a fully populated {@link android.service.autofill.Dataset} if
77 * a response or a dataset is being authenticated respectively.
78 *
79 * <p>
80 * Type: {@link android.service.autofill.FillResponse} or a
81 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +000082 */
83 public static final String EXTRA_AUTHENTICATION_RESULT =
84 "android.view.autofill.extra.AUTHENTICATION_RESULT";
85
Felipe Leme7320ca92017-03-29 15:09:54 -070086 /**
87 * Intent extra: The optional extras provided by the
88 * {@link android.service.autofill.AutofillService}.
89 *
90 * <p>For example, when the service responds to a {@link
91 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
92 * a {@code FillResponse} that requires authentication, the Intent that launches the
93 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -070094 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -070095 *
96 * <p>
97 * Type: {@link android.os.Bundle}
98 */
Felipe Leme37a440fd2017-04-28 10:50:20 -070099 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600100 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700101
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700102 static final String SESSION_ID_TAG = "android:sessionId";
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700103 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
104
Felipe Leme0aa4c502017-04-26 12:36:01 -0700105 /** @hide */ public static final int ACTION_START_SESSION = 1;
106 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
107 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
108 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800109
Felipe Leme9f9ee252017-04-27 13:56:22 -0700110
111 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
112 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
113 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
114
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700115 /** Which bits in an authentication id are used for the dataset id */
116 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
117 /** How many bits in an authentication id are used for the dataset id */
118 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
119 /** @hide The index for an undefined data set */
120 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
121
122 /**
123 * Makes an authentication id from a request id and a dataset id.
124 *
125 * @param requestId The request id.
126 * @param datasetId The dataset id.
127 * @return The authentication id.
128 * @hide
129 */
130 public static int makeAuthenticationId(int requestId, int datasetId) {
131 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
132 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
133 }
134
135 /**
136 * Gets the request id from an authentication id.
137 *
138 * @param authRequestId The authentication id.
139 * @return The request id.
140 * @hide
141 */
142 public static int getRequestIdFromAuthenticationId(int authRequestId) {
143 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
144 }
145
146 /**
147 * Gets the dataset id from an authentication id.
148 *
149 * @param authRequestId The authentication id.
150 * @return The dataset id.
151 * @hide
152 */
153 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
154 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
155 }
156
Felipe Leme4753bb02017-03-22 20:24:00 -0700157 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000158
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700159 /**
160 * There is currently no session running.
161 * {@hide}
162 */
163 public static final int NO_SESSION = Integer.MIN_VALUE;
164
Svet Ganov782043c2017-02-11 00:52:02 +0000165 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700166
167 private final Object mLock = new Object();
168
169 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000170 private IAutoFillManagerClient mServiceClient;
171
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700172 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800173 private AutofillCallback mCallback;
174
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700175 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000176
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700177 @GuardedBy("mLock")
178 private int mSessionId = NO_SESSION;
179
180 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000181 private boolean mEnabled;
182
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700183 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700184 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700185 @Nullable private ParcelableMap mLastAutofilledData;
186
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700187 /** If view tracking is enabled, contains the tracking state */
188 @GuardedBy("mLock")
189 @Nullable private TrackedViews mTrackedViews;
190
Felipe Leme27e20222017-05-18 15:24:11 -0700191 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
192 @GuardedBy("mLock")
193 @Nullable private ArraySet<AutofillId> mFillableIds;
194
Svet Ganov782043c2017-02-11 00:52:02 +0000195 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800196 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000197 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000198 * Asks the client to start an authentication flow.
199 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700200 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000201 * @param intent The authentication intent.
202 * @param fillInIntent The authentication fill-in intent.
203 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700204 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
205 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800206
207 /**
208 * Tells the client this manager has state to be reset.
209 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700210 void autofillCallbackResetableStateAvailable();
211
212 /**
213 * Request showing the autofill UI.
214 *
215 * @param anchor The real view the UI needs to anchor to.
216 * @param width The width of the fill UI content.
217 * @param height The height of the fill UI content.
218 * @param virtualBounds The bounds of the virtual decendant of the anchor.
219 * @param presenter The presenter that controls the fill UI window.
220 * @return Whether the UI was shown.
221 */
222 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
223 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
224
225 /**
226 * Request hiding the autofill UI.
227 *
228 * @return Whether the UI was hidden.
229 */
230 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700231
232 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700233 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700234 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700235 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700236 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700237 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700238
239 /**
240 * Checks is the client is currently visible as understood by autofill.
241 *
242 * @return {@code true} if the client is currently visible
243 */
244 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700245
246 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700247 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700248 *
Phil Weaver846cda932017-06-15 10:10:06 -0700249 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700250 *
Felipe Leme27e20222017-05-18 15:24:11 -0700251 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700252 */
Phil Weaver846cda932017-06-15 10:10:06 -0700253 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700254
255 /**
256 * Finds a view by traversing the hierarchies of the client.
257 *
Phil Weaver846cda932017-06-15 10:10:06 -0700258 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700259 *
260 * @return The view, or {@code null} if not found
261 */
Phil Weaver846cda932017-06-15 10:10:06 -0700262 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700263
264 /**
265 * Runs the specified action on the UI thread.
266 */
267 void runOnUiThread(Runnable action);
Svet Ganov782043c2017-02-11 00:52:02 +0000268 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800269
270 /**
271 * @hide
272 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800273 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800274 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800275 mService = service;
276 }
277
278 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700279 * Restore state after activity lifecycle
280 *
281 * @param savedInstanceState The state to be restored
282 *
283 * {@hide}
284 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700285 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700286 if (!hasAutofillFeature()) {
287 return;
288 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700289 synchronized (mLock) {
290 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
291
292 if (mSessionId != NO_SESSION) {
293 Log.w(TAG, "New session was started before onCreate()");
294 return;
295 }
296
297 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
298
299 if (mSessionId != NO_SESSION) {
300 ensureServiceClientAddedIfNeededLocked();
301
302 final AutofillClient client = getClientLocked();
303 if (client != null) {
304 try {
305 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
306 mContext.getActivityToken(), mServiceClient.asBinder());
307
308 if (!sessionWasRestored) {
309 Log.w(TAG, "Session " + mSessionId + " could not be restored");
310 mSessionId = NO_SESSION;
311 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700312 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700313 Log.d(TAG, "session " + mSessionId + " was restored");
314 }
315
316 client.autofillCallbackResetableStateAvailable();
317 }
318 } catch (RemoteException e) {
319 Log.e(TAG, "Could not figure out if there was an autofill session", e);
320 }
321 }
322 }
323 }
324 }
325
326 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700327 * Called once the client becomes visible.
328 *
329 * @see AutofillClient#isVisibleForAutofill()
330 *
331 * {@hide}
332 */
333 public void onVisibleForAutofill() {
334 synchronized (mLock) {
335 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700336 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700337 }
338 }
339 }
340
341 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700342 * Save state before activity lifecycle
343 *
344 * @param outState Place to store the state
345 *
346 * {@hide}
347 */
348 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700349 if (!hasAutofillFeature()) {
350 return;
351 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700352 synchronized (mLock) {
353 if (mSessionId != NO_SESSION) {
354 outState.putInt(SESSION_ID_TAG, mSessionId);
355 }
356
357 if (mLastAutofilledData != null) {
358 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
359 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700360 }
361 }
362
363 /**
364 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700365 *
366 * <p>Typically used to determine whether the option to explicitly request autofill should
367 * be offered - see {@link #requestAutofill(View)}.
368 *
369 * @return whether autofill is enabled for the current user.
370 */
371 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700372 if (!hasAutofillFeature()) {
373 return false;
374 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700375 synchronized (mLock) {
376 ensureServiceClientAddedIfNeededLocked();
377 return mEnabled;
378 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700379 }
380
381 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700382 * Should always be called from {@link AutofillService#getFillEventHistory()}.
383 *
384 * @hide
385 */
386 @Nullable public FillEventHistory getFillEventHistory() {
387 try {
388 return mService.getFillEventHistory();
389 } catch (RemoteException e) {
390 e.rethrowFromSystemServer();
391 return null;
392 }
393 }
394
395 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700396 * Explicitly requests a new autofill context.
397 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700398 * <p>Normally, the autofill context is automatically started if necessary when
399 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
400 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
401 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700402 *
403 * @param view view requesting the new autofill context.
404 */
405 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700406 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700407 }
408
409 /**
410 * Explicitly requests a new autofill context for virtual views.
411 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700412 * <p>Normally, the autofill context is automatically started if necessary when
413 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
414 * cases where it must be explicitly started. For example, when the virtual view offers an
415 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700416 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700417 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
418 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
419 * the absolute bounds could be calculated by:
420 *
421 * <pre class="prettyprint">
422 * int offset[] = new int[2];
423 * getLocationOnScreen(offset);
424 * Rect absBounds = new Rect(bounds.left + offset[0],
425 * bounds.top + offset[1],
426 * bounds.right + offset[0], bounds.bottom + offset[1]);
427 * </pre>
428 *
429 * @param view the virtual view parent.
430 * @param virtualId id identifying the virtual child inside the parent view.
431 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700432 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700433 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
434 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700435 }
436
Felipe Leme2ac463e2017-03-13 14:06:25 -0700437 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700438 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800439 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700440 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800441 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700442 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700443 notifyViewEntered(view, 0);
444 }
445
446 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700447 if (!hasAutofillFeature()) {
448 return;
449 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700450 AutofillCallback callback = null;
451 synchronized (mLock) {
452 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000453
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700454 if (!mEnabled) {
455 if (mCallback != null) {
456 callback = mCallback;
457 }
458 } else {
459 final AutofillId id = getAutofillId(view);
460 final AutofillValue value = view.getAutofillValue();
461
462 if (mSessionId == NO_SESSION) {
463 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700464 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700465 } else {
466 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700467 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700468 }
Felipe Leme24aae152017-03-15 12:33:01 -0700469 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800470 }
471
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700472 if (callback != null) {
473 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000474 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800475 }
476
477 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700478 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800479 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700480 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800481 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700482 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700483 if (!hasAutofillFeature()) {
484 return;
485 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700486 synchronized (mLock) {
487 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800488
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700489 if (mEnabled && mSessionId != NO_SESSION) {
490 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800491
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700492 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700493 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700494 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800495 }
496 }
497
498 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700499 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700500 *
501 * @param view {@link View} that was exited.
502 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700503 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700504 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
505 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
506 }
507
508 /**
509 * Called when a virtual view's visibility changed.
510 *
511 * @param view {@link View} that was exited.
512 * @param virtualId id identifying the virtual child inside the parent view.
513 * @param isVisible visible if the view is visible in the view hierarchy.
514 */
515 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
516 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
517 }
518
519 /**
520 * Called when a view/virtual view's visibility changed.
521 *
522 * @param view {@link View} that was exited.
523 * @param virtualId id identifying the virtual child inside the parent view.
524 * @param isVisible visible if the view is visible in the view hierarchy.
525 * @param virtual Whether the view is virtual.
526 */
527 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
528 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700529 synchronized (mLock) {
Felipe Leme27e20222017-05-18 15:24:11 -0700530 if (mEnabled && mSessionId != NO_SESSION) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700531 final AutofillId id = virtual ? getAutofillId(view, virtualId)
532 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700533 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700534 if (mFillableIds.contains(id)) {
535 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
536 requestHideFillUi(id, view);
537 }
538 }
539 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700540 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700541 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700542 }
543 }
544 }
545
546 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700547 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800548 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700549 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
550 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
551 * the absolute bounds could be calculated by:
552 *
553 * <pre class="prettyprint">
554 * int offset[] = new int[2];
555 * getLocationOnScreen(offset);
556 * Rect absBounds = new Rect(bounds.left + offset[0],
557 * bounds.top + offset[1],
558 * bounds.right + offset[0], bounds.bottom + offset[1]);
559 * </pre>
560 *
561 * @param view the virtual view parent.
562 * @param virtualId id identifying the virtual child inside the parent view.
563 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800564 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700565 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
566 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700567 }
568
Felipe Leme6dcec872017-05-25 11:24:23 -0700569 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700570 if (!hasAutofillFeature()) {
571 return;
572 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700573 AutofillCallback callback = null;
574 synchronized (mLock) {
575 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000576
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700577 if (!mEnabled) {
578 if (mCallback != null) {
579 callback = mCallback;
580 }
581 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700582 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700583
584 if (mSessionId == NO_SESSION) {
585 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700586 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700587 } else {
588 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700589 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700590 }
Felipe Leme24aae152017-03-15 12:33:01 -0700591 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800592 }
593
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700594 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700595 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700596 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800597 }
598 }
599
600 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700601 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800602 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700603 * @param view the virtual view parent.
604 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800605 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700606 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700607 if (!hasAutofillFeature()) {
608 return;
609 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700610 synchronized (mLock) {
611 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800612
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700613 if (mEnabled && mSessionId != NO_SESSION) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700614 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800615
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700616 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700617 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700618 }
Svet Ganov782043c2017-02-11 00:52:02 +0000619 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800620 }
621
622 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800623 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800624 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800625 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800626 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700627 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700628 if (!hasAutofillFeature()) {
629 return;
630 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700631 AutofillId id = null;
632 boolean valueWasRead = false;
633 AutofillValue value = null;
634
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700635 synchronized (mLock) {
636 // If the session is gone some fields might still be highlighted, hence we have to
637 // remove the isAutofilled property even if no sessions are active.
638 if (mLastAutofilledData == null) {
639 view.setAutofilled(false);
640 } else {
641 id = getAutofillId(view);
642 if (mLastAutofilledData.containsKey(id)) {
643 value = view.getAutofillValue();
644 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700645
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700646 if (Objects.equals(mLastAutofilledData.get(id), value)) {
647 view.setAutofilled(true);
648 } else {
649 view.setAutofilled(false);
650 mLastAutofilledData.remove(id);
651 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700652 } else {
653 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700654 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700655 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700656
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700657 if (!mEnabled || mSessionId == NO_SESSION) {
658 return;
659 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800660
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700661 if (id == null) {
662 id = getAutofillId(view);
663 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700664
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700665 if (!valueWasRead) {
666 value = view.getAutofillValue();
667 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700668
Felipe Leme0aa4c502017-04-26 12:36:01 -0700669 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700670 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800671 }
672
Felipe Lemebab851c2017-02-03 18:45:08 -0800673 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700674 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800675 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700676 * @param view the virtual view parent.
677 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800678 * @param value new value of the child.
679 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700680 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700681 if (!hasAutofillFeature()) {
682 return;
683 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700684 synchronized (mLock) {
685 if (!mEnabled || mSessionId == NO_SESSION) {
686 return;
687 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800688
Felipe Leme6dcec872017-05-25 11:24:23 -0700689 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700690 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700691 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800692 }
693
694 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700695 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800696 *
697 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
698 * call this method after the form is submitted and another page is rendered.
699 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700700 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700701 if (!hasAutofillFeature()) {
702 return;
703 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700704 synchronized (mLock) {
705 if (!mEnabled && mSessionId == NO_SESSION) {
706 return;
707 }
Svet Ganov782043c2017-02-11 00:52:02 +0000708
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700709 finishSessionLocked();
710 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800711 }
712
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700713 /**
714 * Called to indicate the current autofill context should be cancelled.
715 *
716 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
717 * call this method if the user does not post the form but moves to another form in this page.
718 */
719 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700720 if (!hasAutofillFeature()) {
721 return;
722 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700723 synchronized (mLock) {
724 if (!mEnabled && mSessionId == NO_SESSION) {
725 return;
726 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700727
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700728 cancelSessionLocked();
729 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700730 }
731
Svet Ganovf965b872017-04-28 16:34:02 -0700732 /** @hide */
733 public void disableOwnedAutofillServices() {
734 disableAutofillServices();
735 }
736
Svet Ganovf20a0372017-04-10 17:08:05 -0700737 /**
738 * If the app calling this API has enabled autofill services they
739 * will be disabled.
740 */
Svet Ganovf965b872017-04-28 16:34:02 -0700741 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700742 if (!hasAutofillFeature()) {
743 return;
744 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700745 try {
746 mService.disableOwnedAutofillServices(mContext.getUserId());
747 } catch (RemoteException e) {
748 throw e.rethrowFromSystemServer();
749 }
750 }
751
Felipe Lemedb041182017-04-21 17:33:38 -0700752 /**
753 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
754 * enabled for the current user, or {@code false} otherwise.
755 */
756 public boolean hasEnabledAutofillServices() {
757 if (mService == null) return false;
758
759 try {
760 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
761 } catch (RemoteException e) {
762 throw e.rethrowFromSystemServer();
763 }
764 }
765
766 /**
767 * Returns {@code true} if Autofill is supported for this user.
768 *
769 * <p>Autofill is typically supported, but it could be unsupported in cases like:
770 * <ol>
771 * <li>Low-end devices.
772 * <li>Device policy rules that forbid its usage.
773 * </ol>
774 */
775 public boolean isAutofillSupported() {
776 if (mService == null) return false;
777
778 try {
779 return mService.isServiceSupported(mContext.getUserId());
780 } catch (RemoteException e) {
781 throw e.rethrowFromSystemServer();
782 }
783 }
784
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700785 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800786 if (mContext instanceof AutofillClient) {
787 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000788 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800789 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000790 }
791
792 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700793 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700794 if (!hasAutofillFeature()) {
795 return;
796 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700797 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800798 // handling the cases where it's not RESULT_OK: it works fine if the service does not
799 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
800 // service set the extra and returned RESULT_CANCELED...
801
Felipe Leme9f9ee252017-04-27 13:56:22 -0700802 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800803
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700804 synchronized (mLock) {
805 if (mSessionId == NO_SESSION || data == null) {
806 return;
807 }
808 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
809 final Bundle responseData = new Bundle();
810 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
811 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700812 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
813 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700814 } catch (RemoteException e) {
815 Log.e(TAG, "Error delivering authentication result", e);
816 }
Svet Ganov782043c2017-02-11 00:52:02 +0000817 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800818 }
819
Felipe Leme640f30a2017-03-06 15:44:06 -0800820 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -0700821 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800822 }
823
Felipe Leme6dcec872017-05-25 11:24:23 -0700824 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -0700825 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800826 }
827
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700828 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
829 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700830 if (sVerbose) {
831 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Leme2ac463e2017-03-13 14:06:25 -0700832 + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800833 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800834
Felipe Lemebab851c2017-02-03 18:45:08 -0800835 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700836 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -0800837 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800838 mCallback != null, flags, mContext.getOpPackageName());
Felipe Leme0aa4c502017-04-26 12:36:01 -0700839 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800840 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700841 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800842 }
Svet Ganov782043c2017-02-11 00:52:02 +0000843 } catch (RemoteException e) {
844 throw e.rethrowFromSystemServer();
845 }
846 }
847
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700848 private void finishSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700849 if (sVerbose) Log.v(TAG, "finishSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700850
Svet Ganov782043c2017-02-11 00:52:02 +0000851 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700852 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800853 } catch (RemoteException e) {
854 throw e.rethrowFromSystemServer();
855 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700856
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700857 mTrackedViews = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700858 mSessionId = NO_SESSION;
Felipe Lemebab851c2017-02-03 18:45:08 -0800859 }
860
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700861 private void cancelSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700862 if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700863
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700864 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700865 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700866 } catch (RemoteException e) {
867 throw e.rethrowFromSystemServer();
868 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700869
Svet Ganov48f10a22017-04-26 18:49:30 -0700870 resetSessionLocked();
871 }
872
873 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700874 mSessionId = NO_SESSION;
Svet Ganov48f10a22017-04-26 18:49:30 -0700875 mTrackedViews = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700876 }
877
Felipe Leme0aa4c502017-04-26 12:36:01 -0700878 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
879 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700880 if (sVerbose && action != ACTION_VIEW_EXITED) {
881 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
882 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800883 }
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800884
Felipe Leme7f33cd32017-05-11 10:10:49 -0700885 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
886
Felipe Leme3461d3c2017-01-19 08:54:55 -0800887 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -0700888 if (restartIfNecessary) {
889 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
890 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
891 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
892 if (newId != mSessionId) {
893 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
894 mSessionId = newId;
895 final AutofillClient client = getClientLocked();
896 if (client != null) {
897 client.autofillCallbackResetableStateAvailable();
898 }
899 }
900 } else {
901 mService.updateSession(mSessionId, id, bounds, value, action, flags,
902 mContext.getUserId());
903 }
904
Felipe Leme3461d3c2017-01-19 08:54:55 -0800905 } catch (RemoteException e) {
906 throw e.rethrowFromSystemServer();
907 }
908 }
Svet Ganov782043c2017-02-11 00:52:02 +0000909
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700910 private void ensureServiceClientAddedIfNeededLocked() {
911 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +0000912 return;
913 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700914
Svet Ganov782043c2017-02-11 00:52:02 +0000915 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800916 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +0000917 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700918 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
919 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
920 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
921 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +0000922 } catch (RemoteException e) {
923 throw e.rethrowFromSystemServer();
924 }
925 }
926 }
927
Felipe Lemee6010f22017-03-03 11:19:51 -0800928 /**
929 * Registers a {@link AutofillCallback} to receive autofill events.
930 *
931 * @param callback callback to receive events.
932 */
933 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700934 if (!hasAutofillFeature()) {
935 return;
936 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700937 synchronized (mLock) {
938 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800939
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700940 final boolean hadCallback = mCallback != null;
941 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -0800942
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700943 if (!hadCallback) {
944 try {
945 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
946 } catch (RemoteException e) {
947 throw e.rethrowFromSystemServer();
948 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800949 }
950 }
951 }
952
953 /**
954 * Unregisters a {@link AutofillCallback} to receive autofill events.
955 *
956 * @param callback callback to stop receiving events.
957 */
958 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700959 if (!hasAutofillFeature()) {
960 return;
961 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700962 synchronized (mLock) {
963 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800964
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700965 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800966
Felipe Lemee6010f22017-03-03 11:19:51 -0800967 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700968 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -0800969 } catch (RemoteException e) {
970 throw e.rethrowFromSystemServer();
971 }
972 }
973 }
974
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700975 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
976 Rect anchorBounds, IAutofillWindowPresenter presenter) {
977 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -0700978 if (anchor == null) {
979 return;
980 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700981
982 AutofillCallback callback = null;
983 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700984 if (mSessionId == sessionId) {
985 AutofillClient client = getClientLocked();
986
987 if (client != null) {
988 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
989 anchorBounds, presenter) && mCallback != null) {
990 callback = mCallback;
991 }
992 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700993 }
994 }
995
996 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700997 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700998 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700999 AutofillCallback.EVENT_INPUT_SHOWN);
1000 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001001 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001002 }
1003 }
1004 }
1005
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001006 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1007 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001008 synchronized (mLock) {
1009 if (sessionId == mSessionId) {
1010 AutofillClient client = getClientLocked();
1011 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001012 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001013 }
1014 }
1015 }
1016 }
1017
Svet Ganov48f10a22017-04-26 18:49:30 -07001018 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001019 synchronized (mLock) {
1020 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -07001021 if (!mEnabled || resetSession) {
1022 // Reset the session state
1023 resetSessionLocked();
1024 }
1025 if (resetClient) {
1026 // Reset connection to system
1027 mServiceClient = null;
1028 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001029 }
1030 }
1031
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001032 /**
1033 * Sets a view as autofilled if the current value is the {code targetValue}.
1034 *
1035 * @param view The view that is to be autofilled
1036 * @param targetValue The value we want to fill into view
1037 */
1038 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1039 AutofillValue currentValue = view.getAutofillValue();
1040 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001041 synchronized (mLock) {
1042 if (mLastAutofilledData == null) {
1043 mLastAutofilledData = new ParcelableMap(1);
1044 }
1045 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001046 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001047 view.setAutofilled(true);
1048 }
1049 }
1050
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001051 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001052 synchronized (mLock) {
1053 if (sessionId != mSessionId) {
1054 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001055 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001056
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001057 final AutofillClient client = getClientLocked();
1058 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001059 return;
1060 }
1061
1062 final int itemCount = ids.size();
1063 int numApplied = 0;
1064 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001065 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001066
1067 for (int i = 0; i < itemCount; i++) {
1068 final AutofillId id = ids.get(i);
1069 final AutofillValue value = values.get(i);
1070 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001071 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001072 if (view == null) {
1073 Log.w(TAG, "autofill(): no View with id " + viewId);
1074 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001075 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001076 if (id.isVirtual()) {
1077 if (virtualValues == null) {
1078 // Most likely there will be just one view with virtual children.
1079 virtualValues = new ArrayMap<>(1);
1080 }
1081 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1082 if (valuesByParent == null) {
1083 // We don't know the size yet, but usually it will be just a few fields...
1084 valuesByParent = new SparseArray<>(5);
1085 virtualValues.put(view, valuesByParent);
1086 }
1087 valuesByParent.put(id.getVirtualChildId(), value);
1088 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001089 // Mark the view as to be autofilled with 'value'
1090 if (mLastAutofilledData == null) {
1091 mLastAutofilledData = new ParcelableMap(itemCount - i);
1092 }
1093 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001094
1095 view.autofill(value);
1096
1097 // Set as autofilled if the values match now, e.g. when the value was updated
1098 // synchronously.
1099 // If autofill happens async, the view is set to autofilled in
1100 // notifyValueChanged.
1101 setAutofilledIfValuesIs(view, value);
1102
1103 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001104 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001105 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001106
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001107 if (virtualValues != null) {
1108 for (int i = 0; i < virtualValues.size(); i++) {
1109 final View parent = virtualValues.keyAt(i);
1110 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1111 parent.autofill(childrenValues);
1112 numApplied += childrenValues.size();
1113 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001114 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001115
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001116 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1117 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1118 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1119 numApplied);
1120 mMetricsLogger.write(log);
1121 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001122 }
1123
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001124 /**
1125 * Set the tracked views.
1126 *
1127 * @param trackedIds The views to be tracked
1128 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme27e20222017-05-18 15:24:11 -07001129 * @param fillableIds Views that might anchor FillUI.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001130 */
Felipe Leme27e20222017-05-18 15:24:11 -07001131 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1132 boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001133 synchronized (mLock) {
1134 if (mEnabled && mSessionId == sessionId) {
1135 if (saveOnAllViewsInvisible) {
1136 mTrackedViews = new TrackedViews(trackedIds);
1137 } else {
1138 mTrackedViews = null;
1139 }
Felipe Leme27e20222017-05-18 15:24:11 -07001140 if (fillableIds != null) {
1141 if (mFillableIds == null) {
1142 mFillableIds = new ArraySet<>(fillableIds.length);
1143 }
1144 for (AutofillId id : fillableIds) {
1145 mFillableIds.add(id);
1146 }
1147 if (sVerbose) {
1148 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1149 + ", mFillableIds" + mFillableIds);
1150 }
1151 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001152 }
1153 }
1154 }
1155
Felipe Leme27e20222017-05-18 15:24:11 -07001156 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001157 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001158 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001159 if (anchor == null) {
1160 return;
1161 }
Felipe Leme27e20222017-05-18 15:24:11 -07001162 requestHideFillUi(id, anchor);
1163 }
1164
1165 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001166
1167 AutofillCallback callback = null;
1168 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001169 // We do not check the session id for two reasons:
1170 // 1. If local and remote session id are off sync the UI would be stuck shown
1171 // 2. There is a race between the user state being destroyed due the fill
1172 // service being uninstalled and the UI being dismissed.
1173 AutofillClient client = getClientLocked();
1174 if (client != null) {
1175 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1176 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001177 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001178 }
1179 }
1180
1181 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001182 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001183 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001184 AutofillCallback.EVENT_INPUT_HIDDEN);
1185 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001186 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001187 }
1188 }
1189 }
1190
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001191 private void notifyNoFillUi(int sessionId, AutofillId id) {
1192 final View anchor = findView(id);
1193 if (anchor == null) {
1194 return;
1195 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001196
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001197 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001198 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001199 if (mSessionId == sessionId && getClientLocked() != null) {
1200 callback = mCallback;
1201 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001202 }
1203
1204 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001205 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001206 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001207 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1208 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001209 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001210 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001211
Felipe Leme4753bb02017-03-22 20:24:00 -07001212 }
1213 }
1214
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001215 /**
1216 * Get an array of viewIds from a List of {@link AutofillId}.
1217 *
1218 * @param autofillIds The autofill ids to convert
1219 *
1220 * @return The array of viewIds.
1221 */
Felipe Leme27e20222017-05-18 15:24:11 -07001222 // TODO: move to Helper as static method
1223 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1224 final int numIds = autofillIds.length;
1225 final int[] viewIds = new int[numIds];
1226 for (int i = 0; i < numIds; i++) {
1227 viewIds[i] = autofillIds[i].getViewId();
1228 }
1229
1230 return viewIds;
1231 }
1232
1233 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001234 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1235 final int numIds = autofillIds.size();
1236 final int[] viewIds = new int[numIds];
1237 for (int i = 0; i < numIds; i++) {
1238 viewIds[i] = autofillIds.get(i).getViewId();
1239 }
1240
1241 return viewIds;
1242 }
1243
1244 /**
1245 * Find a single view by its id.
1246 *
1247 * @param autofillId The autofill id of the view
1248 *
1249 * @return The view or {@code null} if view was not found
1250 */
1251 private View findView(@NonNull AutofillId autofillId) {
1252 final AutofillClient client = getClientLocked();
1253
1254 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001255 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001256 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001257
Phil Weaver846cda932017-06-15 10:10:06 -07001258 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001259 }
1260
Felipe Lemebb810922017-04-25 15:54:06 -07001261 /** @hide */
1262 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001263 return mService != null;
1264 }
1265
Felipe Leme9876a6f2017-05-30 15:47:28 -07001266 private void post(Runnable runnable) {
1267 final AutofillClient client = getClientLocked();
1268 if (client == null) {
1269 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1270 return;
1271 }
1272 client.runOnUiThread(runnable);
1273 }
1274
Felipe Lemee6010f22017-03-03 11:19:51 -08001275 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001276 * View tracking information. Once all tracked views become invisible the session is finished.
1277 */
1278 private class TrackedViews {
1279 /** Visible tracked views */
1280 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1281
1282 /** Invisible tracked views */
1283 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1284
1285 /**
1286 * Check if set is null or value is in set.
1287 *
1288 * @param set The set or null (== empty set)
1289 * @param value The value that might be in the set
1290 *
1291 * @return {@code true} iff set is not empty and value is in set
1292 */
Felipe Leme27e20222017-05-18 15:24:11 -07001293 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001294 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1295 return set != null && set.contains(value);
1296 }
1297
1298 /**
1299 * Add a value to a set. If set is null, create a new set.
1300 *
1301 * @param set The set or null (== empty set)
1302 * @param valueToAdd The value to add
1303 *
1304 * @return The set including the new value. If set was {@code null}, a set containing only
1305 * the new value.
1306 */
Felipe Leme27e20222017-05-18 15:24:11 -07001307 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001308 @NonNull
1309 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1310 if (set == null) {
1311 set = new ArraySet<>(1);
1312 }
1313
1314 set.add(valueToAdd);
1315
1316 return set;
1317 }
1318
1319 /**
1320 * Remove a value from a set.
1321 *
1322 * @param set The set or null (== empty set)
1323 * @param valueToRemove The value to remove
1324 *
1325 * @return The set without the removed value. {@code null} if set was null, or is empty
1326 * after removal.
1327 */
Felipe Leme27e20222017-05-18 15:24:11 -07001328 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001329 @Nullable
1330 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1331 if (set == null) {
1332 return null;
1333 }
1334
1335 set.remove(valueToRemove);
1336
1337 if (set.isEmpty()) {
1338 return null;
1339 }
1340
1341 return set;
1342 }
1343
1344 /**
1345 * Set the tracked views.
1346 *
1347 * @param trackedIds The views to be tracked
1348 */
Felipe Leme27e20222017-05-18 15:24:11 -07001349 TrackedViews(@Nullable AutofillId[] trackedIds) {
1350 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001351 if (trackedIds != null && client != null) {
1352 final boolean[] isVisible;
1353
1354 if (client.isVisibleForAutofill()) {
1355 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1356 } else {
1357 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001358 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001359 }
1360
Felipe Leme27e20222017-05-18 15:24:11 -07001361 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001362 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001363 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001364
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001365 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001366 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001367 } else {
1368 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1369 }
1370 }
1371 }
1372
Felipe Leme9f9ee252017-04-27 13:56:22 -07001373 if (sVerbose) {
1374 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001375 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1376 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1377 }
1378
1379 if (mVisibleTrackedIds == null) {
1380 finishSessionLocked();
1381 }
1382 }
1383
1384 /**
1385 * Called when a {@link View view's} visibility changes.
1386 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001387 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001388 * @param isVisible visible if the view is visible in the view hierarchy.
1389 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001390 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001391 AutofillClient client = getClientLocked();
1392
Felipe Leme9f9ee252017-04-27 13:56:22 -07001393 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001394 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001395 + isVisible);
1396 }
1397
1398 if (client != null && client.isVisibleForAutofill()) {
1399 if (isVisible) {
1400 if (isInSet(mInvisibleTrackedIds, id)) {
1401 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1402 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1403 }
1404 } else {
1405 if (isInSet(mVisibleTrackedIds, id)) {
1406 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1407 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1408 }
1409 }
1410 }
1411
1412 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001413 if (sVerbose) {
1414 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1415 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001416 finishSessionLocked();
1417 }
1418 }
1419
1420 /**
1421 * Called once the client becomes visible.
1422 *
1423 * @see AutofillClient#isVisibleForAutofill()
1424 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001425 void onVisibleForAutofillLocked() {
1426 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001427 // hence update the visibility state for all views.
1428 AutofillClient client = getClientLocked();
1429 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1430 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1431 if (client != null) {
1432 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001433 final ArrayList<AutofillId> orderedInvisibleIds =
1434 new ArrayList<>(mInvisibleTrackedIds);
1435 final boolean[] isVisible = client.getViewVisibility(
1436 getViewIds(orderedInvisibleIds));
1437
1438 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1439 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1440 final AutofillId id = orderedInvisibleIds.get(i);
1441 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001442 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1443
Felipe Leme9f9ee252017-04-27 13:56:22 -07001444 if (sDebug) {
1445 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001446 }
1447 } else {
1448 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1449 }
1450 }
1451 }
1452
1453 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001454 final ArrayList<AutofillId> orderedVisibleIds =
1455 new ArrayList<>(mVisibleTrackedIds);
1456 final boolean[] isVisible = client.getViewVisibility(
1457 getViewIds(orderedVisibleIds));
1458
1459 final int numVisibleTrackedIds = orderedVisibleIds.size();
1460 for (int i = 0; i < numVisibleTrackedIds; i++) {
1461 final AutofillId id = orderedVisibleIds.get(i);
1462
1463 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001464 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1465 } else {
1466 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1467
Felipe Leme9f9ee252017-04-27 13:56:22 -07001468 if (sDebug) {
1469 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001470 }
1471 }
1472 }
1473 }
1474
1475 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1476 mVisibleTrackedIds = updatedVisibleTrackedIds;
1477 }
1478
1479 if (mVisibleTrackedIds == null) {
1480 finishSessionLocked();
1481 }
1482 }
1483 }
1484
1485 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001486 * Callback for auto-fill related events.
1487 *
1488 * <p>Typically used for applications that display their own "auto-complete" views, so they can
1489 * enable / disable such views when the auto-fill UI affordance is shown / hidden.
1490 */
1491 public abstract static class AutofillCallback {
1492
1493 /** @hide */
1494 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1495 @Retention(RetentionPolicy.SOURCE)
1496 public @interface AutofillEventType {}
1497
1498 /**
1499 * The auto-fill input UI affordance associated with the view was shown.
1500 *
1501 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1502 * should be hidden upon receiving this event.
1503 */
1504 public static final int EVENT_INPUT_SHOWN = 1;
1505
1506 /**
1507 * The auto-fill input UI affordance associated with the view was hidden.
1508 *
1509 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1510 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1511 */
1512 public static final int EVENT_INPUT_HIDDEN = 2;
1513
1514 /**
Felipe Leme24aae152017-03-15 12:33:01 -07001515 * The auto-fill input UI affordance associated with the view won't be shown because
1516 * autofill is not available.
1517 *
1518 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1519 * to avoid flickering, it could shown it upon receiving this event.
1520 */
1521 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1522
1523 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001524 * Called after a change in the autofill state associated with a view.
1525 *
1526 * @param view view associated with the change.
1527 *
1528 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1529 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001530 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1531 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001532
1533 /**
1534 * Called after a change in the autofill state associated with a virtual view.
1535 *
1536 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07001537 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08001538 *
1539 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1540 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001541 public void onAutofillEvent(@NonNull View view, int virtualId,
1542 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07001543 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001544 }
1545
Felipe Leme640f30a2017-03-06 15:44:06 -08001546 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1547 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001548
Felipe Leme640f30a2017-03-06 15:44:06 -08001549 AutofillManagerClient(AutofillManager autofillManager) {
1550 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001551 }
1552
1553 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001554 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001555 final AutofillManager afm = mAfm.get();
1556 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001557 afm.post(() -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001558 }
1559 }
1560
1561 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001562 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001563 final AutofillManager afm = mAfm.get();
1564 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001565 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001566 }
1567 }
1568
1569 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001570 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1571 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001572 final AutofillManager afm = mAfm.get();
1573 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001574 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001575 }
1576 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001577
1578 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001579 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1580 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001581 final AutofillManager afm = mAfm.get();
1582 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001583 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1584 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001585 }
1586 }
1587
1588 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001589 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001590 final AutofillManager afm = mAfm.get();
1591 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001592 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001593 }
1594 }
1595
1596 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001597 public void notifyNoFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001598 final AutofillManager afm = mAfm.get();
1599 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001600 afm.post(() -> afm.notifyNoFillUi(sessionId, id));
Felipe Lemee6010f22017-03-03 11:19:51 -08001601 }
1602 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001603
1604 @Override
1605 public void startIntentSender(IntentSender intentSender) {
1606 final AutofillManager afm = mAfm.get();
1607 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001608 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001609 try {
1610 afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
1611 } catch (IntentSender.SendIntentException e) {
1612 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1613 }
1614 });
1615 }
1616 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001617
1618 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07001619 public void setTrackedViews(int sessionId, AutofillId[] ids,
1620 boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001621 final AutofillManager afm = mAfm.get();
1622 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001623 afm.post(() ->
Felipe Leme27e20222017-05-18 15:24:11 -07001624 afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001625 );
1626 }
1627 }
Svet Ganov782043c2017-02-11 00:52:02 +00001628 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001629}