blob: e85a658a631674088890d02cae8f8921f413f3b4 [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 Lemed633f072017-02-14 10:17:17 -080019import static android.view.autofill.Helper.DEBUG;
Felipe Leme0ab53dc2017-02-23 08:33:18 -080020import static android.view.autofill.Helper.VERBOSE;
Felipe Lemed633f072017-02-14 10:17:17 -080021
Felipe Lemee6010f22017-03-03 11:19:51 -080022import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080023import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080024import android.annotation.Nullable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080025import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000026import android.content.Intent;
27import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080028import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070029import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000030import android.os.Bundle;
Svet Ganov28a2c7e2017-02-21 10:16:16 -080031import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080033import android.os.RemoteException;
Felipe Leme4753bb02017-03-22 20:24:00 -070034import android.util.ArrayMap;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070036import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080037import android.view.View;
Felipe Lemee6010f22017-03-03 11:19:51 -080038import android.view.WindowManagerGlobal;
Felipe Leme3461d3c2017-01-19 08:54:55 -080039
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070040import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070041import com.android.internal.logging.MetricsLogger;
42import com.android.internal.logging.nano.MetricsProto;
43
Felipe Lemee6010f22017-03-03 11:19:51 -080044import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000046import java.lang.ref.WeakReference;
47import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070048import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000049
Felipe Leme3461d3c2017-01-19 08:54:55 -080050/**
51 * App entry point to the AutoFill Framework.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070052 *
53 * <p>It is safe to call into this from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -080054 */
55// TODO(b/33197203): improve this javadoc
Felipe Lemebab851c2017-02-03 18:45:08 -080056//TODO(b/33197203): restrict manager calls to activity
Felipe Leme640f30a2017-03-06 15:44:06 -080057public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -080058
Felipe Leme640f30a2017-03-06 15:44:06 -080059 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -080060
Svet Ganov782043c2017-02-11 00:52:02 +000061 /**
62 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -070063 *
Svet Ganov782043c2017-02-11 00:52:02 +000064 * <p>
65 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +000066 */
67 public static final String EXTRA_ASSIST_STRUCTURE =
68 "android.view.autofill.extra.ASSIST_STRUCTURE";
69
70 /**
71 * Intent extra: The result of an authentication operation. It is
72 * either a fully populated {@link android.service.autofill.FillResponse}
73 * or a fully populated {@link android.service.autofill.Dataset} if
74 * a response or a dataset is being authenticated respectively.
75 *
76 * <p>
77 * Type: {@link android.service.autofill.FillResponse} or a
78 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +000079 */
80 public static final String EXTRA_AUTHENTICATION_RESULT =
81 "android.view.autofill.extra.AUTHENTICATION_RESULT";
82
Felipe Leme7320ca92017-03-29 15:09:54 -070083 /**
84 * Intent extra: The optional extras provided by the
85 * {@link android.service.autofill.AutofillService}.
86 *
87 * <p>For example, when the service responds to a {@link
88 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
89 * a {@code FillResponse} that requires authentication, the Intent that launches the
90 * service authentication will contain the Bundle set by
91 * {@link android.service.autofill.FillResponse.Builder#setExtras(Bundle)} on this extra.
92 *
93 * <p>
94 * Type: {@link android.os.Bundle}
95 */
96 public static final String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
97
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070098 static final String SESSION_ID_TAG = "android:sessionId";
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070099 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
100
Felipe Leme2ac463e2017-03-13 14:06:25 -0700101 // Public flags start from the lowest bit
102 /**
103 * Indicates autofill was explicitly requested by the user.
104 */
105 public static final int FLAG_MANUAL_REQUEST = 0x1;
106
107 // Private flags start from the highest bit
108 /** @hide */ public static final int FLAG_START_SESSION = 0x80000000;
109 /** @hide */ public static final int FLAG_VIEW_ENTERED = 0x40000000;
110 /** @hide */ public static final int FLAG_VIEW_EXITED = 0x20000000;
111 /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x10000000;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800112
Felipe Leme4753bb02017-03-22 20:24:00 -0700113 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000114
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700115 /**
116 * There is currently no session running.
117 * {@hide}
118 */
119 public static final int NO_SESSION = Integer.MIN_VALUE;
120
Svet Ganov782043c2017-02-11 00:52:02 +0000121 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700122
123 private final Object mLock = new Object();
124
125 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000126 private IAutoFillManagerClient mServiceClient;
127
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700128 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800129 private AutofillCallback mCallback;
130
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700131 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000132
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700133 @GuardedBy("mLock")
134 private int mSessionId = NO_SESSION;
135
136 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000137 private boolean mEnabled;
138
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700139 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700140 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700141 @Nullable private ParcelableMap mLastAutofilledData;
142
Svet Ganov782043c2017-02-11 00:52:02 +0000143 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800144 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000145 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000146 * Asks the client to start an authentication flow.
147 *
148 * @param intent The authentication intent.
149 * @param fillInIntent The authentication fill-in intent.
150 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700151 void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800152
153 /**
154 * Tells the client this manager has state to be reset.
155 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700156 void autofillCallbackResetableStateAvailable();
157
158 /**
159 * Request showing the autofill UI.
160 *
161 * @param anchor The real view the UI needs to anchor to.
162 * @param width The width of the fill UI content.
163 * @param height The height of the fill UI content.
164 * @param virtualBounds The bounds of the virtual decendant of the anchor.
165 * @param presenter The presenter that controls the fill UI window.
166 * @return Whether the UI was shown.
167 */
168 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
169 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
170
171 /**
172 * Request hiding the autofill UI.
173 *
174 * @return Whether the UI was hidden.
175 */
176 boolean autofillCallbackRequestHideFillUi();
Svet Ganov782043c2017-02-11 00:52:02 +0000177 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800178
179 /**
180 * @hide
181 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800182 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800183 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800184 mService = service;
185 }
186
187 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700188 * Restore state after activity lifecycle
189 *
190 * @param savedInstanceState The state to be restored
191 *
192 * {@hide}
193 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700194 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700195 if (!hasAutofillFeature()) {
196 return;
197 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700198 synchronized (mLock) {
199 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
200
201 if (mSessionId != NO_SESSION) {
202 Log.w(TAG, "New session was started before onCreate()");
203 return;
204 }
205
206 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
207
208 if (mSessionId != NO_SESSION) {
209 ensureServiceClientAddedIfNeededLocked();
210
211 final AutofillClient client = getClientLocked();
212 if (client != null) {
213 try {
214 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
215 mContext.getActivityToken(), mServiceClient.asBinder());
216
217 if (!sessionWasRestored) {
218 Log.w(TAG, "Session " + mSessionId + " could not be restored");
219 mSessionId = NO_SESSION;
220 } else {
221 if (DEBUG) {
222 Log.d(TAG, "session " + mSessionId + " was restored");
223 }
224
225 client.autofillCallbackResetableStateAvailable();
226 }
227 } catch (RemoteException e) {
228 Log.e(TAG, "Could not figure out if there was an autofill session", e);
229 }
230 }
231 }
232 }
233 }
234
235 /**
236 * Set window future popup windows should be attached to.
237 *
238 * @param windowToken The window the popup windows should be attached to
239 *
240 * {@hide}
241 */
242 public void onAttachedToWindow(@NonNull IBinder windowToken) {
Svet Ganov43574b02017-04-12 09:25:20 -0700243 if (!hasAutofillFeature()) {
244 return;
245 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700246 synchronized (mLock) {
247 if (mSessionId == NO_SESSION) {
248 return;
249 }
250
251 try {
252 mService.setWindow(mSessionId, windowToken);
253 } catch (RemoteException e) {
254 Log.e(TAG, "Could not attach window to session " + mSessionId);
255 }
256 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700257 }
258
259 /**
260 * Save state before activity lifecycle
261 *
262 * @param outState Place to store the state
263 *
264 * {@hide}
265 */
266 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700267 if (!hasAutofillFeature()) {
268 return;
269 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700270 synchronized (mLock) {
271 if (mSessionId != NO_SESSION) {
272 outState.putInt(SESSION_ID_TAG, mSessionId);
273 }
274
275 if (mLastAutofilledData != null) {
276 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
277 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700278 }
279 }
280
281 /**
282 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700283 *
284 * <p>Typically used to determine whether the option to explicitly request autofill should
285 * be offered - see {@link #requestAutofill(View)}.
286 *
287 * @return whether autofill is enabled for the current user.
288 */
289 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700290 if (!hasAutofillFeature()) {
291 return false;
292 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700293 synchronized (mLock) {
294 ensureServiceClientAddedIfNeededLocked();
295 return mEnabled;
296 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700297 }
298
299 /**
300 * Explicitly requests a new autofill context.
301 *
302 * <p>Normally, the autofill context is automatically started when autofillable views are
303 * focused, but this method should be used in the cases where it must be explicitly requested,
304 * like a view that provides a contextual menu allowing users to autofill the activity.
305 *
306 * @param view view requesting the new autofill context.
307 */
308 public void requestAutofill(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700309 if (!hasAutofillFeature()) {
310 return;
311 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700312 synchronized (mLock) {
313 ensureServiceClientAddedIfNeededLocked();
Felipe Leme2ac463e2017-03-13 14:06:25 -0700314
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700315 if (!mEnabled) {
316 return;
317 }
318
319 final AutofillId id = getAutofillId(view);
320 final AutofillValue value = view.getAutofillValue();
321
322 startSessionLocked(id, view.getWindowToken(), null, value, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700323 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700324 }
325
326 /**
327 * Explicitly requests a new autofill context for virtual views.
328 *
329 * <p>Normally, the autofill context is automatically started when autofillable views are
330 * focused, but this method should be used in the cases where it must be explicitly requested,
331 * like a virtual view that provides a contextual menu allowing users to autofill the activity.
332 *
333 * @param view the {@link View} whose descendant is the virtual view.
334 * @param childId id identifying the virtual child inside the view.
335 * @param bounds child boundaries, relative to the top window.
336 */
337 public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
Svet Ganov43574b02017-04-12 09:25:20 -0700338 if (!hasAutofillFeature()) {
339 return;
340 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700341 synchronized (mLock) {
342 ensureServiceClientAddedIfNeededLocked();
Felipe Leme2ac463e2017-03-13 14:06:25 -0700343
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700344 if (!mEnabled) {
345 return;
346 }
347
348 final AutofillId id = getAutofillId(view, childId);
349 startSessionLocked(id, view.getWindowToken(), bounds, null, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700350 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700351 }
352
353
354 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700355 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800356 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700357 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800358 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700359 public void notifyViewEntered(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700360 if (!hasAutofillFeature()) {
361 return;
362 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700363 AutofillCallback callback = null;
364 synchronized (mLock) {
365 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000366
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700367 if (!mEnabled) {
368 if (mCallback != null) {
369 callback = mCallback;
370 }
371 } else {
372 final AutofillId id = getAutofillId(view);
373 final AutofillValue value = view.getAutofillValue();
374
375 if (mSessionId == NO_SESSION) {
376 // Starts new session.
377 startSessionLocked(id, view.getWindowToken(), null, value, 0);
378 } else {
379 // Update focus on existing session.
380 updateSessionLocked(id, null, value, FLAG_VIEW_ENTERED);
381 }
Felipe Leme24aae152017-03-15 12:33:01 -0700382 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800383 }
384
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700385 if (callback != null) {
386 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000387 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800388 }
389
390 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700391 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800392 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700393 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800394 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700395 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700396 if (!hasAutofillFeature()) {
397 return;
398 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700399 synchronized (mLock) {
400 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800401
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700402 if (mEnabled && mSessionId != NO_SESSION) {
403 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800404
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700405 // Update focus on existing session.
406 updateSessionLocked(id, null, null, FLAG_VIEW_EXITED);
407 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800408 }
409 }
410
411 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700412 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800413 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700414 * @param view the {@link View} whose descendant is the virtual view.
415 * @param childId id identifying the virtual child inside the view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800416 * @param bounds child boundaries, relative to the top window.
Felipe Lemebab851c2017-02-03 18:45:08 -0800417 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700418 public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
Svet Ganov43574b02017-04-12 09:25:20 -0700419 if (!hasAutofillFeature()) {
420 return;
421 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700422 AutofillCallback callback = null;
423 synchronized (mLock) {
424 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000425
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700426 if (!mEnabled) {
427 if (mCallback != null) {
428 callback = mCallback;
429 }
430 } else {
431 final AutofillId id = getAutofillId(view, childId);
432
433 if (mSessionId == NO_SESSION) {
434 // Starts new session.
435 startSessionLocked(id, view.getWindowToken(), bounds, null, 0);
436 } else {
437 // Update focus on existing session.
438 updateSessionLocked(id, bounds, null, FLAG_VIEW_ENTERED);
439 }
Felipe Leme24aae152017-03-15 12:33:01 -0700440 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800441 }
442
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700443 if (callback != null) {
444 callback.onAutofillEvent(view, childId,
445 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800446 }
447 }
448
449 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700450 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800451 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700452 * @param view the {@link View} whose descendant is the virtual view.
453 * @param childId id identifying the virtual child inside the view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800454 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700455 public void notifyViewExited(@NonNull View view, int childId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700456 if (!hasAutofillFeature()) {
457 return;
458 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700459 synchronized (mLock) {
460 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800461
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700462 if (mEnabled && mSessionId != NO_SESSION) {
463 final AutofillId id = getAutofillId(view, childId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800464
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700465 // Update focus on existing session.
466 updateSessionLocked(id, null, null, FLAG_VIEW_EXITED);
467 }
Svet Ganov782043c2017-02-11 00:52:02 +0000468 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800469 }
470
471 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800472 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800473 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800474 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800475 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700476 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700477 if (!hasAutofillFeature()) {
478 return;
479 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700480 AutofillId id = null;
481 boolean valueWasRead = false;
482 AutofillValue value = null;
483
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700484 synchronized (mLock) {
485 // If the session is gone some fields might still be highlighted, hence we have to
486 // remove the isAutofilled property even if no sessions are active.
487 if (mLastAutofilledData == null) {
488 view.setAutofilled(false);
489 } else {
490 id = getAutofillId(view);
491 if (mLastAutofilledData.containsKey(id)) {
492 value = view.getAutofillValue();
493 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700494
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700495 if (Objects.equals(mLastAutofilledData.get(id), value)) {
496 view.setAutofilled(true);
497 } else {
498 view.setAutofilled(false);
499 mLastAutofilledData.remove(id);
500 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700501 } else {
502 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700503 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700504 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700505
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700506 if (!mEnabled || mSessionId == NO_SESSION) {
507 return;
508 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800509
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700510 if (id == null) {
511 id = getAutofillId(view);
512 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700513
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700514 if (!valueWasRead) {
515 value = view.getAutofillValue();
516 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700517
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700518 updateSessionLocked(id, null, value, FLAG_VALUE_CHANGED);
519 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800520 }
521
Felipe Lemebab851c2017-02-03 18:45:08 -0800522 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800523 * Called to indicate the value of an autofillable virtual {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800524 *
Felipe Leme2ac463e2017-03-13 14:06:25 -0700525 * @param view the {@link View} whose descendant is the virtual view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800526 * @param childId id identifying the virtual child inside the parent view.
527 * @param value new value of the child.
528 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700529 public void notifyValueChanged(View view, int childId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700530 if (!hasAutofillFeature()) {
531 return;
532 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700533 synchronized (mLock) {
534 if (!mEnabled || mSessionId == NO_SESSION) {
535 return;
536 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800537
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700538 final AutofillId id = getAutofillId(view, childId);
539 updateSessionLocked(id, null, value, FLAG_VALUE_CHANGED);
540 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800541 }
542
543 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700544 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800545 *
546 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
547 * call this method after the form is submitted and another page is rendered.
548 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700549 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700550 if (!hasAutofillFeature()) {
551 return;
552 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700553 synchronized (mLock) {
554 if (!mEnabled && mSessionId == NO_SESSION) {
555 return;
556 }
Svet Ganov782043c2017-02-11 00:52:02 +0000557
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700558 finishSessionLocked();
559 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800560 }
561
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700562 /**
563 * Called to indicate the current autofill context should be cancelled.
564 *
565 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
566 * call this method if the user does not post the form but moves to another form in this page.
567 */
568 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700569 if (!hasAutofillFeature()) {
570 return;
571 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700572 synchronized (mLock) {
573 if (!mEnabled && mSessionId == NO_SESSION) {
574 return;
575 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700576
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700577 cancelSessionLocked();
578 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700579 }
580
Svet Ganovf20a0372017-04-10 17:08:05 -0700581 /**
582 * If the app calling this API has enabled autofill services they
583 * will be disabled.
584 */
585 public void disableOwnedAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700586 if (!hasAutofillFeature()) {
587 return;
588 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700589 try {
590 mService.disableOwnedAutofillServices(mContext.getUserId());
591 } catch (RemoteException e) {
592 throw e.rethrowFromSystemServer();
593 }
594 }
595
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700596 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800597 if (mContext instanceof AutofillClient) {
598 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000599 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800600 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000601 }
602
603 /** @hide */
604 public void onAuthenticationResult(Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700605 if (!hasAutofillFeature()) {
606 return;
607 }
Felipe Lemed633f072017-02-14 10:17:17 -0800608 // TODO(b/33197203): the result code is being ignored, so this method is not reliably
609 // handling the cases where it's not RESULT_OK: it works fine if the service does not
610 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
611 // service set the extra and returned RESULT_CANCELED...
612
613 if (DEBUG) Log.d(TAG, "onAuthenticationResult(): d=" + data);
614
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700615 synchronized (mLock) {
616 if (mSessionId == NO_SESSION || data == null) {
617 return;
618 }
619 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
620 final Bundle responseData = new Bundle();
621 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
622 try {
623 mService.setAuthenticationResult(responseData, mSessionId, mContext.getUserId());
624 } catch (RemoteException e) {
625 Log.e(TAG, "Error delivering authentication result", e);
626 }
Svet Ganov782043c2017-02-11 00:52:02 +0000627 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800628 }
629
Felipe Leme640f30a2017-03-06 15:44:06 -0800630 private static AutofillId getAutofillId(View view) {
631 return new AutofillId(view.getAccessibilityViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800632 }
633
Felipe Leme640f30a2017-03-06 15:44:06 -0800634 private static AutofillId getAutofillId(View parent, int childId) {
635 return new AutofillId(parent.getAccessibilityViewId(), childId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800636 }
637
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700638 private void startSessionLocked(@NonNull AutofillId id, @NonNull IBinder windowToken,
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800639 @NonNull Rect bounds, @NonNull AutofillValue value, int flags) {
Svet Ganov782043c2017-02-11 00:52:02 +0000640 if (DEBUG) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700641 Log.d(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Leme2ac463e2017-03-13 14:06:25 -0700642 + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800643 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800644
Felipe Lemebab851c2017-02-03 18:45:08 -0800645 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700646 mSessionId = mService.startSession(mContext.getActivityToken(), windowToken,
Felipe Lemee6010f22017-03-03 11:19:51 -0800647 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800648 mCallback != null, flags, mContext.getOpPackageName());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700649 AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800650 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700651 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800652 }
Svet Ganov782043c2017-02-11 00:52:02 +0000653 } catch (RemoteException e) {
654 throw e.rethrowFromSystemServer();
655 }
656 }
657
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700658 private void finishSessionLocked() {
Svet Ganov782043c2017-02-11 00:52:02 +0000659 if (DEBUG) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700660 Log.d(TAG, "finishSessionLocked()");
Svet Ganov782043c2017-02-11 00:52:02 +0000661 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700662
Svet Ganov782043c2017-02-11 00:52:02 +0000663 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700664 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800665 } catch (RemoteException e) {
666 throw e.rethrowFromSystemServer();
667 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700668
669 mSessionId = NO_SESSION;
Felipe Lemebab851c2017-02-03 18:45:08 -0800670 }
671
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700672 private void cancelSessionLocked() {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700673 if (DEBUG) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700674 Log.d(TAG, "cancelSessionLocked()");
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700675 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700676
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700677 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700678 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700679 } catch (RemoteException e) {
680 throw e.rethrowFromSystemServer();
681 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700682
683 mSessionId = NO_SESSION;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700684 }
685
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700686 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) {
Svet Ganov782043c2017-02-11 00:52:02 +0000687 if (DEBUG) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700688 if (VERBOSE || (flags & FLAG_VIEW_EXITED) != 0) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700689 Log.d(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
690 + ", value=" + value + ", flags=" + flags);
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800691 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800692 }
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800693
Felipe Leme3461d3c2017-01-19 08:54:55 -0800694 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700695 mService.updateSession(mSessionId, id, bounds, value, flags, mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -0800696 } catch (RemoteException e) {
697 throw e.rethrowFromSystemServer();
698 }
699 }
Svet Ganov782043c2017-02-11 00:52:02 +0000700
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700701 private void ensureServiceClientAddedIfNeededLocked() {
702 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +0000703 return;
704 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700705
Svet Ganov782043c2017-02-11 00:52:02 +0000706 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800707 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +0000708 try {
709 mEnabled = mService.addClient(mServiceClient, mContext.getUserId());
710 } catch (RemoteException e) {
711 throw e.rethrowFromSystemServer();
712 }
713 }
714 }
715
Felipe Lemee6010f22017-03-03 11:19:51 -0800716 /**
717 * Registers a {@link AutofillCallback} to receive autofill events.
718 *
719 * @param callback callback to receive events.
720 */
721 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700722 if (!hasAutofillFeature()) {
723 return;
724 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700725 synchronized (mLock) {
726 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800727
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700728 final boolean hadCallback = mCallback != null;
729 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -0800730
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700731 if (!hadCallback) {
732 try {
733 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
734 } catch (RemoteException e) {
735 throw e.rethrowFromSystemServer();
736 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800737 }
738 }
739 }
740
741 /**
742 * Unregisters a {@link AutofillCallback} to receive autofill events.
743 *
744 * @param callback callback to stop receiving events.
745 */
746 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700747 if (!hasAutofillFeature()) {
748 return;
749 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700750 synchronized (mLock) {
751 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800752
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700753 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800754
Felipe Lemee6010f22017-03-03 11:19:51 -0800755 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700756 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -0800757 } catch (RemoteException e) {
758 throw e.rethrowFromSystemServer();
759 }
760 }
761 }
762
Felipe Leme4753bb02017-03-22 20:24:00 -0700763 private void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height,
764 Rect anchorBounds, IAutofillWindowPresenter presenter) {
765 final View anchor = findAchorView(windowToken, id);
766 if (anchor == null) {
767 return;
768 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700769
770 AutofillCallback callback = null;
771 synchronized (mLock) {
772 if (getClientLocked().autofillCallbackRequestShowFillUi(anchor, width, height,
773 anchorBounds, presenter) && mCallback != null) {
774 callback = mCallback;
775 }
776 }
777
778 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700779 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700781 AutofillCallback.EVENT_INPUT_SHOWN);
782 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700783 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -0700784 }
785 }
786 }
787
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700788 /**
789 * Sets a view as autofilled if the current value is the {code targetValue}.
790 *
791 * @param view The view that is to be autofilled
792 * @param targetValue The value we want to fill into view
793 */
794 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
795 AutofillValue currentValue = view.getAutofillValue();
796 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700797 synchronized (mLock) {
798 if (mLastAutofilledData == null) {
799 mLastAutofilledData = new ParcelableMap(1);
800 }
801 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700802 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700803 view.setAutofilled(true);
804 }
805 }
806
Felipe Leme4753bb02017-03-22 20:24:00 -0700807 private void handleAutofill(IBinder windowToken, List<AutofillId> ids,
808 List<AutofillValue> values) {
809 final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
810 if (root == null) {
Felipe Lemee6010f22017-03-03 11:19:51 -0800811 return;
812 }
813
Felipe Leme4753bb02017-03-22 20:24:00 -0700814 final int itemCount = ids.size();
815 int numApplied = 0;
816 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
817
818 for (int i = 0; i < itemCount; i++) {
819 final AutofillId id = ids.get(i);
820 final AutofillValue value = values.get(i);
821 final int viewId = id.getViewId();
822 final View view = root.findViewByAccessibilityIdTraversal(viewId);
823 if (view == null) {
824 Log.w(TAG, "autofill(): no View with id " + viewId);
825 continue;
826 }
827 if (id.isVirtual()) {
828 if (virtualValues == null) {
829 // Most likely there will be just one view with virtual children.
830 virtualValues = new ArrayMap<>(1);
831 }
832 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
833 if (valuesByParent == null) {
834 // We don't know the size yet, but usually it will be just a few fields...
835 valuesByParent = new SparseArray<>(5);
836 virtualValues.put(view, valuesByParent);
837 }
838 valuesByParent.put(id.getVirtualChildId(), value);
839 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700840 synchronized (mLock) {
841 // Mark the view as to be autofilled with 'value'
842 if (mLastAutofilledData == null) {
843 mLastAutofilledData = new ParcelableMap(itemCount - i);
844 }
845 mLastAutofilledData.put(id, value);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700846 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700847
Felipe Leme955e2522017-03-29 17:47:58 -0700848 view.autofill(value);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700849
850 // Set as autofilled if the values match now, e.g. when the value was updated
851 // synchronously.
852 // If autofill happens async, the view is set to autofilled in notifyValueChanged.
853 setAutofilledIfValuesIs(view, value);
854
Felipe Leme955e2522017-03-29 17:47:58 -0700855 numApplied++;
Felipe Leme4753bb02017-03-22 20:24:00 -0700856 }
857 }
858
859 if (virtualValues != null) {
860 for (int i = 0; i < virtualValues.size(); i++) {
861 final View parent = virtualValues.keyAt(i);
862 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
Felipe Leme955e2522017-03-29 17:47:58 -0700863 parent.autofill(childrenValues);
864 numApplied += childrenValues.size();
Felipe Leme4753bb02017-03-22 20:24:00 -0700865 }
866 }
867
868 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
869 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
870 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
871 mMetricsLogger.write(log);
872 }
873
874 private void requestHideFillUi(IBinder windowToken, AutofillId id) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700875 final View anchor = findAchorView(windowToken, id);
876
877 AutofillCallback callback = null;
878 synchronized (mLock) {
879 if (getClientLocked().autofillCallbackRequestHideFillUi() && mCallback != null) {
880 callback = mCallback;
881 }
882 }
883
884 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700885 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700886 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700887 AutofillCallback.EVENT_INPUT_HIDDEN);
888 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700889 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -0700890 }
891 }
892 }
893
894 private void notifyNoFillUi(IBinder windowToken, AutofillId id) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700895 final View anchor = findAchorView(windowToken, id);
896
897 AutofillCallback callback;
898 synchronized (mLock) {
899 callback = mCallback;
900 }
901
902 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700903 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700904 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700905 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
906 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700907 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -0700908 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700909
Felipe Leme4753bb02017-03-22 20:24:00 -0700910 }
911 }
912
913 private View findAchorView(IBinder windowToken, AutofillId id) {
Felipe Lemee6010f22017-03-03 11:19:51 -0800914 final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
915 if (root == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700916 Log.w(TAG, "no window with token " + windowToken);
917 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800918 }
919 final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
920 if (view == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700921 Log.w(TAG, "no view with id " + id);
922 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800923 }
Felipe Leme4753bb02017-03-22 20:24:00 -0700924 return view;
Felipe Lemee6010f22017-03-03 11:19:51 -0800925 }
926
Svet Ganov43574b02017-04-12 09:25:20 -0700927 private boolean hasAutofillFeature() {
928 return mService != null;
929 }
930
Felipe Lemee6010f22017-03-03 11:19:51 -0800931 /**
932 * Callback for auto-fill related events.
933 *
934 * <p>Typically used for applications that display their own "auto-complete" views, so they can
935 * enable / disable such views when the auto-fill UI affordance is shown / hidden.
936 */
937 public abstract static class AutofillCallback {
938
939 /** @hide */
940 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
941 @Retention(RetentionPolicy.SOURCE)
942 public @interface AutofillEventType {}
943
944 /**
945 * The auto-fill input UI affordance associated with the view was shown.
946 *
947 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
948 * should be hidden upon receiving this event.
949 */
950 public static final int EVENT_INPUT_SHOWN = 1;
951
952 /**
953 * The auto-fill input UI affordance associated with the view was hidden.
954 *
955 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
956 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
957 */
958 public static final int EVENT_INPUT_HIDDEN = 2;
959
960 /**
Felipe Leme24aae152017-03-15 12:33:01 -0700961 * The auto-fill input UI affordance associated with the view won't be shown because
962 * autofill is not available.
963 *
964 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
965 * to avoid flickering, it could shown it upon receiving this event.
966 */
967 public static final int EVENT_INPUT_UNAVAILABLE = 3;
968
969 /**
Felipe Lemee6010f22017-03-03 11:19:51 -0800970 * Called after a change in the autofill state associated with a view.
971 *
972 * @param view view associated with the change.
973 *
974 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
975 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700976 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
977 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800978
979 /**
980 * Called after a change in the autofill state associated with a virtual view.
981 *
982 * @param view parent view associated with the change.
983 * @param childId id identifying the virtual child inside the parent view.
984 *
985 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
986 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700987 public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
988 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800989 }
990
Felipe Leme640f30a2017-03-06 15:44:06 -0800991 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
992 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +0000993
Felipe Leme640f30a2017-03-06 15:44:06 -0800994 AutofillManagerClient(AutofillManager autofillManager) {
995 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +0000996 }
997
998 @Override
999 public void setState(boolean enabled) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001000 final AutofillManager afm = mAfm.get();
1001 if (afm != null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001002 afm.mContext.getMainThreadHandler().post(() -> {
1003 synchronized (afm.mLock) {
1004 afm.mEnabled = enabled;
1005 }
1006 });
Svet Ganov782043c2017-02-11 00:52:02 +00001007 }
1008 }
1009
1010 @Override
Felipe Leme4753bb02017-03-22 20:24:00 -07001011 public void autofill(IBinder windowToken, List<AutofillId> ids,
1012 List<AutofillValue> values) {
Svet Ganov782043c2017-02-11 00:52:02 +00001013 // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
1014 // dataset.extras to service
Felipe Leme640f30a2017-03-06 15:44:06 -08001015 final AutofillManager afm = mAfm.get();
1016 if (afm != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001017 afm.mContext.getMainThreadHandler().post(() ->
1018 afm.handleAutofill(windowToken, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001019 }
1020 }
1021
1022 @Override
1023 public void authenticate(IntentSender intent, Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001024 final AutofillManager afm = mAfm.get();
1025 if (afm != null) {
1026 afm.mContext.getMainThreadHandler().post(() -> {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001027 if (afm.getClientLocked() != null) {
1028 afm.getClientLocked().autofillCallbackAuthenticate(intent, fillInIntent);
Svet Ganov782043c2017-02-11 00:52:02 +00001029 }
1030 });
1031 }
1032 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001033
1034 @Override
Felipe Leme4753bb02017-03-22 20:24:00 -07001035 public void requestShowFillUi(IBinder windowToken, AutofillId id,
1036 int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001037 final AutofillManager afm = mAfm.get();
1038 if (afm != null) {
1039 afm.mContext.getMainThreadHandler().post(() -> {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001040 if (afm.getClientLocked() != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001041 afm.requestShowFillUi(windowToken, id, width,
1042 height, anchorBounds, presenter);
1043 }
1044 });
1045 }
1046 }
1047
1048 @Override
1049 public void requestHideFillUi(IBinder windowToken, AutofillId id) {
1050 final AutofillManager afm = mAfm.get();
1051 if (afm != null) {
1052 afm.mContext.getMainThreadHandler().post(() -> {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001053 if (afm.getClientLocked() != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001054 afm.requestHideFillUi(windowToken, id);
1055 }
1056 });
1057 }
1058 }
1059
1060 @Override
1061 public void notifyNoFillUi(IBinder windowToken, AutofillId id) {
1062 final AutofillManager afm = mAfm.get();
1063 if (afm != null) {
1064 afm.mContext.getMainThreadHandler().post(() -> {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001065 if (afm.getClientLocked() != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001066 afm.notifyNoFillUi(windowToken, id);
Felipe Lemee6010f22017-03-03 11:19:51 -08001067 }
1068 });
1069 }
1070 }
Svet Ganov782043c2017-02-11 00:52:02 +00001071 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001072}