1 /*
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 
17 package android.autofillservice.cts;
18 
19 import static android.app.Activity.RESULT_CANCELED;
20 import static android.app.Activity.RESULT_OK;
21 import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
22 import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER;
23 import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE;
24 import static android.autofillservice.cts.Helper.ID_PASSWORD;
25 import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
26 import static android.autofillservice.cts.Helper.ID_USERNAME;
27 import static android.autofillservice.cts.Helper.assertNumberOfChildren;
28 import static android.autofillservice.cts.Helper.assertTextAndValue;
29 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
30 import static android.autofillservice.cts.Helper.assertValue;
31 import static android.autofillservice.cts.Helper.dumpStructure;
32 import static android.autofillservice.cts.Helper.eventually;
33 import static android.autofillservice.cts.Helper.findNodeByResourceId;
34 import static android.autofillservice.cts.Helper.runShellCommand;
35 import static android.autofillservice.cts.Helper.setUserComplete;
36 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
37 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
38 import static android.autofillservice.cts.LoginActivity.AUTHENTICATION_MESSAGE;
39 import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
40 import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
41 import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
42 import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
43 import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
44 import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
45 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
46 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
47 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
48 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS;
49 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
50 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
51 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
52 import static android.text.InputType.TYPE_NULL;
53 import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
54 import static android.view.View.IMPORTANT_FOR_AUTOFILL_NO;
55 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
56 
57 import static com.google.common.truth.Truth.assertThat;
58 import static com.google.common.truth.Truth.assertWithMessage;
59 
60 import android.app.PendingIntent;
61 import android.app.assist.AssistStructure.ViewNode;
62 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
63 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
64 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
65 import android.content.BroadcastReceiver;
66 import android.content.Context;
67 import android.content.Intent;
68 import android.content.IntentFilter;
69 import android.content.IntentSender;
70 import android.graphics.Color;
71 import android.os.Bundle;
72 import android.service.autofill.FillEventHistory;
73 import android.service.autofill.FillResponse;
74 import android.service.autofill.SaveInfo;
75 import android.support.test.rule.ActivityTestRule;
76 import android.support.test.uiautomator.UiObject2;
77 import android.view.View;
78 import android.view.View.AccessibilityDelegate;
79 import android.view.ViewGroup;
80 import android.view.WindowManager;
81 import android.view.accessibility.AccessibilityNodeInfo;
82 import android.view.accessibility.AccessibilityNodeProvider;
83 import android.view.autofill.AutofillManager;
84 import android.view.autofill.AutofillValue;
85 import android.widget.RemoteViews;
86 
87 import org.junit.After;
88 import org.junit.Before;
89 import org.junit.Rule;
90 import org.junit.Test;
91 
92 import java.util.concurrent.CountDownLatch;
93 import java.util.concurrent.TimeUnit;
94 
95 /**
96  * This is the test case covering most scenarios - other test cases will cover characteristics
97  * specific to that test's activity (for example, custom views).
98  */
99 public class LoginActivityTest extends AutoFillServiceTestCase {
100 
101     @Rule
102     public final ActivityTestRule<LoginActivity> mActivityRule = new ActivityTestRule<LoginActivity>(
103             LoginActivity.class);
104 
105     private LoginActivity mActivity;
106 
107     @Before
setActivity()108     public void setActivity() {
109         mActivity = mActivityRule.getActivity();
110     }
111 
112     @After
finishWelcomeActivity()113     public void finishWelcomeActivity() {
114         WelcomeActivity.finishIt();
115     }
116 
117     @Test
testAutoFillNoDatasets()118     public void testAutoFillNoDatasets() throws Exception {
119         // Set service.
120         enableService();
121 
122         // Set expectations.
123         sReplier.addResponse(NO_RESPONSE);
124 
125         // Trigger autofill.
126         mActivity.onUsername(View::requestFocus);
127 
128         // Test connection lifecycle.
129         waitUntilConnected();
130         sReplier.getNextFillRequest();
131 
132         // Make sure UI is not shown.
133         sUiBot.assertNoDatasets();
134 
135         // Try to trigger it again...
136 
137         mActivity.onPassword(View::requestFocus);
138         // ...and make sure it didn't
139         sUiBot.assertNoDatasets();
140         sReplier.assertNumberUnhandledFillRequests(0);
141 
142         // Test connection lifecycle.
143         waitUntilDisconnected();
144     }
145 
146     @Test
testAutofillManuallyAfterServiceReturnedNoDatasets()147     public void testAutofillManuallyAfterServiceReturnedNoDatasets() throws Exception {
148         // Set service.
149         enableService();
150 
151         // Set expectations.
152         sReplier.addResponse(NO_RESPONSE);
153 
154         // Trigger autofill.
155         mActivity.onUsername(View::requestFocus);
156         sReplier.getNextFillRequest();
157 
158         // Make sure UI is not shown.
159         sUiBot.assertNoDatasets();
160 
161         // Try again, forcing it
162         sReplier.addResponse(new CannedDataset.Builder()
163                 .setField(ID_USERNAME, "dude")
164                 .setField(ID_PASSWORD, "sweet")
165                 .setPresentation(createPresentation("The Dude"))
166                 .build());
167         mActivity.expectAutoFill("dude", "sweet");
168 
169         mActivity.forceAutofillOnUsername();
170 
171         final FillRequest fillRequest = sReplier.getNextFillRequest();
172         assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
173 
174         // Selects the dataset.
175         sUiBot.selectDataset("The Dude");
176 
177         // Check the results.
178         mActivity.assertAutoFilled();
179     }
180 
181     @Test
testAutofillManuallyAndSaveAfterServiceReturnedNoDatasets()182     public void testAutofillManuallyAndSaveAfterServiceReturnedNoDatasets() throws Exception {
183         // Set service.
184         enableService();
185 
186         // Set expectations.
187         sReplier.addResponse(NO_RESPONSE);
188 
189         // Trigger autofill.
190         mActivity.onUsername(View::requestFocus);
191         sReplier.getNextFillRequest();
192 
193         // Make sure UI is not shown.
194         sUiBot.assertNoDatasets();
195         sReplier.assertNumberUnhandledFillRequests(0);
196         mActivity.onPassword(View::requestFocus);
197         sUiBot.assertNoDatasets();
198         sReplier.assertNumberUnhandledFillRequests(0);
199 
200         // Try again, forcing it
201         saveOnlyTest(true);
202     }
203 
204     @Test
testAutoFillOneDataset()205     public void testAutoFillOneDataset() throws Exception {
206         // Set service.
207         enableService();
208 
209         // Set expectations.
210         sReplier.addResponse(new CannedDataset.Builder()
211                 .setField(ID_USERNAME, "dude")
212                 .setField(ID_PASSWORD, "sweet")
213                 .setPresentation(createPresentation("The Dude"))
214                 .build());
215         mActivity.expectAutoFill("dude", "sweet");
216 
217         // Dynamically set password to make sure it's sanitized.
218         mActivity.onPassword((v) -> v.setText("I AM GROOT"));
219 
220         // Trigger auto-fill.
221         mActivity.onUsername(View::requestFocus);
222 
223         // Auto-fill it.
224         sUiBot.selectDataset("The Dude");
225 
226         // Check the results.
227         mActivity.assertAutoFilled();
228 
229         // Sanity checks.
230 
231         // Make sure input was sanitized.
232         final FillRequest request = sReplier.getNextFillRequest();
233         assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
234         assertTextIsSanitized(request.structure, ID_PASSWORD);
235 
236         // Make sure initial focus was properly set.
237         assertWithMessage("Username node is not focused").that(
238                 findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue();
239         assertWithMessage("Password node is focused").that(
240                 findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
241     }
242 
243     @Test
testAutoFillTwoDatasetsSameNumberOfFields()244     public void testAutoFillTwoDatasetsSameNumberOfFields() throws Exception {
245         // Set service.
246         enableService();
247 
248         // Set expectations.
249         sReplier.addResponse(new CannedFillResponse.Builder()
250                 .addDataset(new CannedDataset.Builder()
251                         .setField(ID_USERNAME, "dude")
252                         .setField(ID_PASSWORD, "sweet")
253                         .setPresentation(createPresentation("The Dude"))
254                         .build())
255                 .addDataset(new CannedDataset.Builder()
256                         .setField(ID_USERNAME, "DUDE")
257                         .setField(ID_PASSWORD, "SWEET")
258                         .setPresentation(createPresentation("THE DUDE"))
259                         .build())
260                 .build());
261         mActivity.expectAutoFill("dude", "sweet");
262 
263         // Trigger auto-fill.
264         mActivity.onUsername(View::requestFocus);
265         sReplier.getNextFillRequest();
266 
267         // Make sure all datasets are available...
268         sUiBot.assertDatasets("The Dude", "THE DUDE");
269 
270         // ... on all fields.
271         mActivity.onPassword(View::requestFocus);
272         sUiBot.assertDatasets("The Dude", "THE DUDE");
273 
274         // Auto-fill it.
275         sUiBot.selectDataset("The Dude");
276 
277         // Check the results.
278         mActivity.assertAutoFilled();
279     }
280 
281     @Test
testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsAll()282     public void testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsAll() throws Exception {
283         autoFillTwoDatasetsUnevenNumberOfFieldsTest(true);
284     }
285 
286     @Test
testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsOne()287     public void testAutoFillTwoDatasetsUnevenNumberOfFieldsFillsOne() throws Exception {
288         autoFillTwoDatasetsUnevenNumberOfFieldsTest(false);
289     }
290 
autoFillTwoDatasetsUnevenNumberOfFieldsTest(boolean fillsAll)291     private void autoFillTwoDatasetsUnevenNumberOfFieldsTest(boolean fillsAll) throws Exception {
292         // Set service.
293         enableService();
294 
295         // Set expectations.
296         sReplier.addResponse(new CannedFillResponse.Builder()
297                 .addDataset(new CannedDataset.Builder()
298                         .setField(ID_USERNAME, "dude")
299                         .setField(ID_PASSWORD, "sweet")
300                         .setPresentation(createPresentation("The Dude"))
301                         .build())
302                 .addDataset(new CannedDataset.Builder()
303                         .setField(ID_USERNAME, "DUDE")
304                         .setPresentation(createPresentation("THE DUDE"))
305                         .build())
306                 .build());
307         if (fillsAll) {
308             mActivity.expectAutoFill("dude", "sweet");
309         } else {
310             mActivity.expectAutoFill("DUDE");
311         }
312 
313         // Trigger auto-fill.
314         mActivity.onUsername(View::requestFocus);
315         sReplier.getNextFillRequest();
316 
317         // Make sure all datasets are available on username...
318         sUiBot.assertDatasets("The Dude", "THE DUDE");
319 
320         // ... but just one for password
321         mActivity.onPassword(View::requestFocus);
322         sUiBot.assertDatasets("The Dude");
323 
324         // Auto-fill it.
325         mActivity.onUsername(View::requestFocus);
326         sUiBot.assertDatasets("The Dude", "THE DUDE");
327         if (fillsAll) {
328             sUiBot.selectDataset("The Dude");
329         } else {
330             sUiBot.selectDataset("THE DUDE");
331         }
332 
333         // Check the results.
334         mActivity.assertAutoFilled();
335     }
336 
337     @Test
testAutoFillWhenViewHasChildAccessibilityNodes()338     public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
339         mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
340             @Override
341             public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
342                 return new AccessibilityNodeProvider() {
343                     @Override
344                     public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
345                         final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
346                         if (virtualViewId == View.NO_ID) {
347                             info.addChild(v, 108);
348                         }
349                         return info;
350                     }
351                 };
352             }
353         }));
354 
355         testAutoFillOneDataset();
356     }
357 
358     @Test
testAutoFillOneDatasetAndMoveFocusAround()359     public void testAutoFillOneDatasetAndMoveFocusAround() throws Exception {
360         // Set service.
361         enableService();
362 
363         // Set expectations.
364         sReplier.addResponse(new CannedDataset.Builder()
365                 .setField(ID_USERNAME, "dude")
366                 .setField(ID_PASSWORD, "sweet")
367                 .setPresentation(createPresentation("The Dude"))
368                 .build());
369         mActivity.expectAutoFill("dude", "sweet");
370 
371         // Trigger auto-fill.
372         mActivity.onUsername(View::requestFocus);
373         sReplier.getNextFillRequest();
374 
375         // Make sure tapping on other fields from the dataset does not trigger it again
376         mActivity.onPassword(View::requestFocus);
377         sReplier.assertNumberUnhandledFillRequests(0);
378 
379         mActivity.onUsername(View::requestFocus);
380         sReplier.assertNumberUnhandledFillRequests(0);
381 
382         // Auto-fill it.
383         sUiBot.selectDataset("The Dude");
384 
385         // Check the results.
386         mActivity.assertAutoFilled();
387 
388         // Make sure tapping on other fields from the dataset does not trigger it again
389         mActivity.onPassword(View::requestFocus);
390         mActivity.onUsername(View::requestFocus);
391     }
392 
393     @Test
testUiNotShownAfterAutofilled()394     public void testUiNotShownAfterAutofilled() throws Exception {
395         // Set service.
396         enableService();
397 
398         // Set expectations.
399         sReplier.addResponse(new CannedDataset.Builder()
400                 .setField(ID_USERNAME, "dude")
401                 .setField(ID_PASSWORD, "sweet")
402                 .setPresentation(createPresentation("The Dude"))
403                 .build());
404         mActivity.expectAutoFill("dude", "sweet");
405 
406         // Trigger auto-fill.
407         mActivity.onUsername(View::requestFocus);
408         sReplier.getNextFillRequest();
409         sUiBot.selectDataset("The Dude");
410 
411         // Check the results.
412         mActivity.assertAutoFilled();
413 
414         // Make sure tapping on autofilled field does not trigger it again
415         mActivity.onPassword(View::requestFocus);
416         sUiBot.assertNoDatasets();
417 
418         mActivity.onUsername(View::requestFocus);
419         sUiBot.assertNoDatasets();
420     }
421 
422     @Test
testAutofillCallbacks()423     public void testAutofillCallbacks() throws Exception {
424         // Set service.
425         enableService();
426         final MyAutofillCallback callback = mActivity.registerCallback();
427 
428         // Set expectations.
429         sReplier.addResponse(new CannedDataset.Builder()
430                 .setField(ID_USERNAME, "dude")
431                 .setField(ID_PASSWORD, "sweet")
432                 .setPresentation(createPresentation("The Dude"))
433                 .build());
434         mActivity.expectAutoFill("dude", "sweet");
435 
436         // Trigger auto-fill.
437         mActivity.onUsername(View::requestFocus);
438         sReplier.getNextFillRequest();
439         final View username = mActivity.getUsername();
440         final View password = mActivity.getPassword();
441 
442         callback.assertUiShownEvent(username);
443 
444         mActivity.onPassword(View::requestFocus);
445         callback.assertUiHiddenEvent(username);
446         callback.assertUiShownEvent(password);
447 
448         mActivity.onUsername(View::requestFocus);
449         mActivity.unregisterCallback();
450         callback.assertNumberUnhandledEvents(0);
451 
452         // Auto-fill it.
453         sUiBot.selectDataset("The Dude");
454 
455         // Check the results.
456         mActivity.assertAutoFilled();
457     }
458 
459     @Test
testAutofillCallbackDisabled()460     public void testAutofillCallbackDisabled() throws Exception {
461         // Set service.
462         disableService();
463 
464         final MyAutofillCallback callback = mActivity.registerCallback();
465 
466         // Trigger auto-fill.
467         mActivity.onUsername(View::requestFocus);
468 
469         // Assert callback was called
470         final View username = mActivity.getUsername();
471         callback.assertUiUnavailableEvent(username);
472     }
473 
474     @Test
testAutofillCallbackNoDatasets()475     public void testAutofillCallbackNoDatasets() throws Exception {
476         callbackUnavailableTest(NO_RESPONSE);
477     }
478 
479     @Test
testAutofillCallbackNoDatasetsButSaveInfo()480     public void testAutofillCallbackNoDatasetsButSaveInfo() throws Exception {
481         callbackUnavailableTest(new CannedFillResponse.Builder()
482                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
483                 .build());
484     }
485 
callbackUnavailableTest(CannedFillResponse response)486     private void callbackUnavailableTest(CannedFillResponse response) throws Exception {
487         // Set service.
488         enableService();
489         final MyAutofillCallback callback = mActivity.registerCallback();
490 
491         // Set expectations.
492         sReplier.addResponse(response);
493 
494         // Trigger auto-fill.
495         mActivity.onUsername(View::requestFocus);
496         sReplier.getNextFillRequest();
497 
498         // Auto-fill it.
499         sUiBot.assertNoDatasets();
500 
501         // Assert callback was called
502         final View username = mActivity.getUsername();
503         callback.assertUiUnavailableEvent(username);
504     }
505 
506     @Test
testAutoFillOneDatasetAndSave()507     public void testAutoFillOneDatasetAndSave() throws Exception {
508         // Set service.
509         enableService();
510 
511         // Set expectations.
512         final Bundle extras = new Bundle();
513         extras.putString("numbers", "4815162342");
514 
515         sReplier.addResponse(new CannedFillResponse.Builder()
516                 .addDataset(new CannedDataset.Builder()
517                         .setField(ID_USERNAME, "dude")
518                         .setField(ID_PASSWORD, "sweet")
519                         .setPresentation(createPresentation("The Dude"))
520                         .build())
521                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
522                 .setExtras(extras)
523                 .build());
524         mActivity.expectAutoFill("dude", "sweet");
525 
526         // Trigger auto-fill.
527         mActivity.onUsername(View::requestFocus);
528 
529         // Since this is a Presubmit test, wait for connection to avoid flakiness.
530         waitUntilConnected();
531 
532         sReplier.getNextFillRequest();
533 
534         // Auto-fill it.
535         sUiBot.selectDataset("The Dude");
536 
537         // Check the results.
538         mActivity.assertAutoFilled();
539 
540         // Try to login, it will fail.
541         final String loginMessage = mActivity.tapLogin();
542 
543         assertWithMessage("Wrong login msg").that(loginMessage).isEqualTo(AUTHENTICATION_MESSAGE);
544 
545         // Set right password...
546         mActivity.onPassword((v) -> v.setText("dude"));
547 
548         // ... and try again
549         final String expectedMessage = getWelcomeMessage("dude");
550         final String actualMessage = mActivity.tapLogin();
551         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
552 
553         // Assert the snack bar is shown and tap "Save".
554         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
555 
556         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
557 
558         // Assert value of expected fields - should not be sanitized.
559         final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
560         assertTextAndValue(username, "dude");
561         final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
562         assertTextAndValue(password, "dude");
563 
564         // Make sure extras were passed back on onSave()
565         assertThat(saveRequest.data).isNotNull();
566         final String extraValue = saveRequest.data.getString("numbers");
567         assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
568 
569         // Sanity check: once saved, the session should be finished.
570         assertNoDanglingSessions();
571     }
572 
573     @Test
testAutoFillOneDatasetAndSaveHidingOverlays()574     public void testAutoFillOneDatasetAndSaveHidingOverlays() throws Exception {
575         // Set service.
576         enableService();
577 
578         // Set expectations.
579         final Bundle extras = new Bundle();
580         extras.putString("numbers", "4815162342");
581 
582         sReplier.addResponse(new CannedFillResponse.Builder()
583                 .addDataset(new CannedDataset.Builder()
584                         .setField(ID_USERNAME, "dude")
585                         .setField(ID_PASSWORD, "sweet")
586                         .setPresentation(createPresentation("The Dude"))
587                         .build())
588                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
589                 .setExtras(extras)
590                 .build());
591         mActivity.expectAutoFill("dude", "sweet");
592 
593         // Trigger auto-fill.
594         mActivity.onUsername(View::requestFocus);
595 
596         // Since this is a Presubmit test, wait for connection to avoid flakiness.
597         waitUntilConnected();
598 
599         sReplier.getNextFillRequest();
600 
601         // Add an overlay on top of the whole screen
602         final View[] overlay = new View[1];
603         try {
604             // Allow ourselves to add overlays
605             runShellCommand("appops set " + getContext().getPackageName()
606                     + " SYSTEM_ALERT_WINDOW allow");
607 
608             // Make sure the fill UI is shown.
609             sUiBot.assertDatasets("The Dude");
610 
611             final CountDownLatch latch = new CountDownLatch(1);
612 
613             mActivity.runOnUiThread(() -> {
614                 // This overlay is focusable, full-screen, which should block interaction
615                 // with the fill UI unless the platform successfully hides overlays.
616                 final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
617                 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
618                 params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
619                 params.width = ViewGroup.LayoutParams.MATCH_PARENT;
620                 params.height = ViewGroup.LayoutParams.MATCH_PARENT;
621 
622                 final View view = new View(getContext()) {
623                     @Override
624                     protected void onAttachedToWindow() {
625                         super.onAttachedToWindow();
626                         latch.countDown();
627                     }
628                 };
629                 view.setBackgroundColor(Color.RED);
630                 WindowManager windowManager = getContext().getSystemService(WindowManager.class);
631                 windowManager.addView(view, params);
632                 overlay[0] = view;
633             });
634 
635             // Wait for the window being added.
636             assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
637 
638             // Auto-fill it.
639             sUiBot.selectDataset("The Dude");
640 
641             // Check the results.
642             mActivity.assertAutoFilled();
643 
644             // Try to login, it will fail.
645             final String loginMessage = mActivity.tapLogin();
646 
647             assertWithMessage("Wrong login msg").that(loginMessage).isEqualTo(
648                     AUTHENTICATION_MESSAGE);
649 
650             // Set right password...
651             mActivity.onPassword((v) -> v.setText("dude"));
652 
653             // ... and try again
654             final String expectedMessage = getWelcomeMessage("dude");
655             final String actualMessage = mActivity.tapLogin();
656             assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
657 
658             // Assert the snack bar is shown and tap "Save".
659             sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
660 
661             final SaveRequest saveRequest = sReplier.getNextSaveRequest();
662 
663             // Assert value of expected fields - should not be sanitized.
664             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
665             assertTextAndValue(username, "dude");
666             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
667             assertTextAndValue(password, "dude");
668 
669             // Make sure extras were passed back on onSave()
670             assertThat(saveRequest.data).isNotNull();
671             final String extraValue = saveRequest.data.getString("numbers");
672             assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
673 
674             // Sanity check: once saved, the session should be finished.
675             assertNoDanglingSessions();
676         } finally {
677             // Make sure we can no longer add overlays
678             runShellCommand("appops set " + getContext().getPackageName()
679                     + " SYSTEM_ALERT_WINDOW ignore");
680             // Make sure the overlay is removed
681             mActivity.runOnUiThread(() -> {
682                 WindowManager windowManager = getContext().getSystemService(WindowManager.class);
683                 windowManager.removeView(overlay[0]);
684             });
685         }
686     }
687 
688     @Test
testAutoFillMultipleDatasetsPickFirst()689     public void testAutoFillMultipleDatasetsPickFirst() throws Exception {
690         multipleDatasetsTest(1);
691     }
692 
693     @Test
testAutoFillMultipleDatasetsPickSecond()694     public void testAutoFillMultipleDatasetsPickSecond() throws Exception {
695         multipleDatasetsTest(2);
696     }
697 
698     @Test
testAutoFillMultipleDatasetsPickThird()699     public void testAutoFillMultipleDatasetsPickThird() throws Exception {
700         multipleDatasetsTest(3);
701     }
702 
multipleDatasetsTest(int number)703     private void multipleDatasetsTest(int number) throws Exception {
704         // Set service.
705         enableService();
706 
707         // Set expectations.
708         sReplier.addResponse(new CannedFillResponse.Builder()
709                 .addDataset(new CannedDataset.Builder()
710                         .setField(ID_USERNAME, "mr_plow")
711                         .setField(ID_PASSWORD, "D'OH!")
712                         .setPresentation(createPresentation("Mr Plow"))
713                         .build())
714                 .addDataset(new CannedDataset.Builder()
715                         .setField(ID_USERNAME, "el barto")
716                         .setField(ID_PASSWORD, "aycaramba!")
717                         .setPresentation(createPresentation("El Barto"))
718                         .build())
719                 .addDataset(new CannedDataset.Builder()
720                         .setField(ID_USERNAME, "mr sparkle")
721                         .setField(ID_PASSWORD, "Aw3someP0wer")
722                         .setPresentation(createPresentation("Mr Sparkle"))
723                         .build())
724                 .build());
725         final String name;
726 
727         switch (number) {
728             case 1:
729                 name = "Mr Plow";
730                 mActivity.expectAutoFill("mr_plow", "D'OH!");
731                 break;
732             case 2:
733                 name = "El Barto";
734                 mActivity.expectAutoFill("el barto", "aycaramba!");
735                 break;
736             case 3:
737                 name = "Mr Sparkle";
738                 mActivity.expectAutoFill("mr sparkle", "Aw3someP0wer");
739                 break;
740             default:
741                 throw new IllegalArgumentException("invalid dataset number: " + number);
742         }
743 
744         // Trigger auto-fill.
745         mActivity.onUsername(View::requestFocus);
746         sReplier.getNextFillRequest();
747 
748         // Make sure all datasets are shown.
749         final UiObject2 picker = sUiBot.assertDatasets("Mr Plow", "El Barto", "Mr Sparkle");
750 
751         // Auto-fill it.
752         sUiBot.selectDataset(picker, name);
753 
754         // Check the results.
755         mActivity.assertAutoFilled();
756     }
757 
758     /**
759      * Tests the scenario where the service uses custom remote views for different fields (username
760      * and password).
761      */
762     @Test
testAutofillOneDatasetCustomPresentation()763     public void testAutofillOneDatasetCustomPresentation() throws Exception {
764         // Set service.
765         enableService();
766 
767         // Set expectations.
768         sReplier.addResponse(new CannedDataset.Builder()
769                 .setField(ID_USERNAME, "dude",
770                         createPresentation("The Dude"))
771                 .setField(ID_PASSWORD, "sweet",
772                         createPresentation("Dude's password"))
773                 .build());
774         mActivity.expectAutoFill("dude", "sweet");
775 
776         // Trigger auto-fill.
777         mActivity.onUsername(View::requestFocus);
778         sReplier.getNextFillRequest();
779 
780         // Check initial field.
781         sUiBot.assertDatasets("The Dude");
782 
783         // Then move around...
784         mActivity.onPassword(View::requestFocus);
785         sUiBot.assertDatasets("Dude's password");
786         mActivity.onUsername(View::requestFocus);
787         sUiBot.assertDatasets("The Dude");
788 
789         // Auto-fill it.
790         mActivity.onPassword(View::requestFocus);
791         sUiBot.selectDataset("Dude's password");
792 
793         // Check the results.
794         mActivity.assertAutoFilled();
795     }
796 
797     /**
798      * Tests the scenario where the service uses custom remote views for different fields (username
799      * and password) and the dataset itself, and each dataset has the same number of fields.
800      */
801     @Test
testAutofillMultipleDatasetsCustomPresentations()802     public void testAutofillMultipleDatasetsCustomPresentations() throws Exception {
803         // Set service.
804         enableService();
805 
806         // Set expectations.
807         sReplier.addResponse(new CannedFillResponse.Builder()
808                 .addDataset(new CannedDataset.Builder(createPresentation("Dataset1"))
809                         .setField(ID_USERNAME, "user1") // no presentation
810                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
811                         .build())
812                 .addDataset(new CannedDataset.Builder()
813                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
814                         .setField(ID_PASSWORD, "pass2") // no presentation
815                         .setPresentation(createPresentation("Dataset2"))
816                         .build())
817                 .build());
818         mActivity.expectAutoFill("user1", "pass1");
819 
820         // Trigger auto-fill.
821         mActivity.onUsername(View::requestFocus);
822         sReplier.getNextFillRequest();
823 
824         // Check initial field.
825         sUiBot.assertDatasets("Dataset1", "User2");
826 
827         // Then move around...
828         mActivity.onPassword(View::requestFocus);
829         sUiBot.assertDatasets("Pass1", "Dataset2");
830         mActivity.onUsername(View::requestFocus);
831         sUiBot.assertDatasets("Dataset1", "User2");
832 
833         // Auto-fill it.
834         mActivity.onPassword(View::requestFocus);
835         sUiBot.selectDataset("Pass1");
836 
837         // Check the results.
838         mActivity.assertAutoFilled();
839     }
840 
841     /**
842      * Tests the scenario where the service uses custom remote views for different fields (username
843      * and password), and each dataset has the same number of fields.
844      */
845     @Test
testAutofillMultipleDatasetsCustomPresentationSameFields()846     public void testAutofillMultipleDatasetsCustomPresentationSameFields() throws Exception {
847         // Set service.
848         enableService();
849 
850         // Set expectations.
851         sReplier.addResponse(new CannedFillResponse.Builder()
852                 .addDataset(new CannedDataset.Builder()
853                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
854                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
855                         .build())
856                 .addDataset(new CannedDataset.Builder()
857                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
858                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
859                         .build())
860                 .build());
861         mActivity.expectAutoFill("user1", "pass1");
862 
863         // Trigger auto-fill.
864         mActivity.onUsername(View::requestFocus);
865         sReplier.getNextFillRequest();
866 
867         // Check initial field.
868         sUiBot.assertDatasets("User1", "User2");
869 
870         // Then move around...
871         mActivity.onPassword(View::requestFocus);
872         sUiBot.assertDatasets("Pass1", "Pass2");
873         mActivity.onUsername(View::requestFocus);
874         sUiBot.assertDatasets("User1", "User2");
875 
876         // Auto-fill it.
877         mActivity.onPassword(View::requestFocus);
878         sUiBot.selectDataset("Pass1");
879 
880         // Check the results.
881         mActivity.assertAutoFilled();
882     }
883 
884     /**
885      * Tests the scenario where the service uses custom remote views for different fields (username
886      * and password), but each dataset has a different number of fields.
887      */
888     @Test
testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()889     public void testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()
890             throws Exception {
891         // Set service.
892         enableService();
893 
894         // Set expectations.
895         sReplier.addResponse(new CannedFillResponse.Builder()
896                 .addDataset(new CannedDataset.Builder()
897                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
898                         .build())
899                 .addDataset(new CannedDataset.Builder()
900                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
901                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
902                         .build())
903                 .build());
904         mActivity.expectAutoFill("user2", "pass2");
905 
906         // Trigger auto-fill.
907         mActivity.onUsername(View::requestFocus);
908         sReplier.getNextFillRequest();
909 
910         // Check initial field.
911         sUiBot.assertDatasets("User1", "User2");
912 
913         // Then move around...
914         mActivity.onPassword(View::requestFocus);
915         sUiBot.assertDatasets("Pass2");
916         mActivity.onUsername(View::requestFocus);
917         sUiBot.assertDatasets("User1", "User2");
918 
919         // Auto-fill it.
920         sUiBot.selectDataset("User2");
921 
922         // Check the results.
923         mActivity.assertAutoFilled();
924     }
925 
926     /**
927      * Tests the scenario where the service uses custom remote views for different fields (username
928      * and password), but each dataset has a different number of fields.
929      */
930     @Test
testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()931     public void testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()
932             throws Exception {
933         // Set service.
934         enableService();
935 
936         // Set expectations.
937         sReplier.addResponse(new CannedFillResponse.Builder()
938                 .addDataset(new CannedDataset.Builder()
939                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
940                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
941                         .build())
942                 .addDataset(new CannedDataset.Builder()
943                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
944                         .build())
945                 .build());
946         mActivity.expectAutoFill("user1", "pass1");
947 
948         // Trigger auto-fill.
949         mActivity.onUsername(View::requestFocus);
950         sReplier.getNextFillRequest();
951 
952         // Check initial field.
953         sUiBot.assertDatasets("User1");
954 
955         // Then move around...
956         mActivity.onPassword(View::requestFocus);
957         sUiBot.assertDatasets("Pass1", "Pass2");
958         mActivity.onUsername(View::requestFocus);
959         sUiBot.assertDatasets("User1");
960 
961         // Auto-fill it.
962         sUiBot.selectDataset("User1");
963 
964         // Check the results.
965         mActivity.assertAutoFilled();
966     }
967 
968     @Test
filterText()969     public void filterText() throws Exception {
970         final String AA = "Two A's";
971         final String AB = "A and B";
972         final String B = "Only B";
973 
974         enableService();
975 
976         // Set expectations.
977         sReplier.addResponse(new CannedFillResponse.Builder()
978                 .addDataset(new CannedDataset.Builder()
979                         .setField(ID_USERNAME, "aa")
980                         .setPresentation(createPresentation(AA))
981                         .build())
982                 .addDataset(new CannedDataset.Builder()
983                         .setField(ID_USERNAME, "ab")
984                         .setPresentation(createPresentation(AB))
985                         .build())
986                 .addDataset(new CannedDataset.Builder()
987                         .setField(ID_USERNAME, "b")
988                         .setPresentation(createPresentation(B))
989                         .build())
990                 .build());
991 
992         // Trigger auto-fill.
993         mActivity.onUsername(View::requestFocus);
994         sReplier.getNextFillRequest();
995 
996         // With no filter text all datasets should be shown
997         sUiBot.assertDatasets(AA, AB, B);
998 
999         // Only two datasets start with 'a'
1000         runShellCommand("input keyevent KEYCODE_A");
1001         sUiBot.assertDatasets(AA, AB);
1002 
1003         // Only one dataset start with 'aa'
1004         runShellCommand("input keyevent KEYCODE_A");
1005         sUiBot.assertDatasets(AA);
1006 
1007         // Only two datasets start with 'a'
1008         runShellCommand("input keyevent KEYCODE_DEL");
1009         sUiBot.assertDatasets(AA, AB);
1010 
1011         // With no filter text all datasets should be shown
1012         runShellCommand("input keyevent KEYCODE_DEL");
1013         sUiBot.assertDatasets(AA, AB, B);
1014 
1015         // No dataset start with 'aaa'
1016         runShellCommand("input keyevent KEYCODE_A");
1017         runShellCommand("input keyevent KEYCODE_A");
1018         runShellCommand("input keyevent KEYCODE_A");
1019         sUiBot.assertNoDatasets();
1020     }
1021 
1022     @Test
filterTextNullValuesAlwaysMatched()1023     public void filterTextNullValuesAlwaysMatched() throws Exception {
1024         final String AA = "Two A's";
1025         final String AB = "A and B";
1026         final String B = "Only B";
1027 
1028         enableService();
1029 
1030         // Set expectations.
1031         sReplier.addResponse(new CannedFillResponse.Builder()
1032                 .addDataset(new CannedDataset.Builder()
1033                         .setField(ID_USERNAME, "aa")
1034                         .setPresentation(createPresentation(AA))
1035                         .build())
1036                 .addDataset(new CannedDataset.Builder()
1037                         .setField(ID_USERNAME, "ab")
1038                         .setPresentation(createPresentation(AB))
1039                         .build())
1040                 .addDataset(new CannedDataset.Builder()
1041                         .setField(ID_USERNAME, (String) null)
1042                         .setPresentation(createPresentation(B))
1043                         .build())
1044                 .build());
1045 
1046         // Trigger auto-fill.
1047         mActivity.onUsername(View::requestFocus);
1048         sReplier.getNextFillRequest();
1049 
1050         // With no filter text all datasets should be shown
1051         sUiBot.assertDatasets(AA, AB, B);
1052 
1053         // Two datasets start with 'a' and one with null value always shown
1054         runShellCommand("input keyevent KEYCODE_A");
1055         sUiBot.assertDatasets(AA, AB, B);
1056 
1057         // One dataset start with 'aa' and one with null value always shown
1058         runShellCommand("input keyevent KEYCODE_A");
1059         sUiBot.assertDatasets(AA, B);
1060 
1061         // Two datasets start with 'a' and one with null value always shown
1062         runShellCommand("input keyevent KEYCODE_DEL");
1063         sUiBot.assertDatasets(AA, AB, B);
1064 
1065         // With no filter text all datasets should be shown
1066         runShellCommand("input keyevent KEYCODE_DEL");
1067         sUiBot.assertDatasets(AA, AB, B);
1068 
1069         // No dataset start with 'aaa' and one with null value always shown
1070         runShellCommand("input keyevent KEYCODE_A");
1071         runShellCommand("input keyevent KEYCODE_A");
1072         runShellCommand("input keyevent KEYCODE_A");
1073         sUiBot.assertDatasets(B);
1074     }
1075 
1076     @Test
filterTextDifferentPrefixes()1077     public void filterTextDifferentPrefixes() throws Exception {
1078         final String A = "aaa";
1079         final String B = "bra";
1080         final String C = "cadabra";
1081 
1082         enableService();
1083 
1084         // Set expectations.
1085         sReplier.addResponse(new CannedFillResponse.Builder()
1086                 .addDataset(new CannedDataset.Builder()
1087                         .setField(ID_USERNAME, A)
1088                         .setPresentation(createPresentation(A))
1089                         .build())
1090                 .addDataset(new CannedDataset.Builder()
1091                         .setField(ID_USERNAME, B)
1092                         .setPresentation(createPresentation(B))
1093                         .build())
1094                 .addDataset(new CannedDataset.Builder()
1095                         .setField(ID_USERNAME, C)
1096                         .setPresentation(createPresentation(C))
1097                         .build())
1098                 .build());
1099 
1100         // Trigger auto-fill.
1101         mActivity.onUsername(View::requestFocus);
1102         sReplier.getNextFillRequest();
1103 
1104         // With no filter text all datasets should be shown
1105         sUiBot.assertDatasets(A, B, C);
1106 
1107         mActivity.onUsername((v) -> v.setText("a"));
1108         sUiBot.assertDatasets(A);
1109 
1110         mActivity.onUsername((v) -> v.setText("b"));
1111         sUiBot.assertDatasets(B);
1112 
1113         mActivity.onUsername((v) -> v.setText("c"));
1114         sUiBot.assertDatasets(C);
1115     }
1116 
1117     @Test
testSaveOnly()1118     public void testSaveOnly() throws Exception {
1119         saveOnlyTest(false);
1120     }
1121 
1122     @Test
testSaveOnlyTriggeredManually()1123     public void testSaveOnlyTriggeredManually() throws Exception {
1124         saveOnlyTest(false);
1125     }
1126 
saveOnlyTest(boolean manually)1127     private void saveOnlyTest(boolean manually) throws Exception {
1128         enableService();
1129 
1130         // Set expectations.
1131         sReplier.addResponse(new CannedFillResponse.Builder()
1132                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1133                 .build());
1134 
1135         // Trigger auto-fill.
1136         if (manually) {
1137             mActivity.forceAutofillOnUsername();
1138         } else {
1139             mActivity.onUsername(View::requestFocus);
1140         }
1141 
1142         // Sanity check.
1143         sUiBot.assertNoDatasets();
1144 
1145         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1146         // the session started
1147         sReplier.getNextFillRequest();
1148 
1149         // Set credentials...
1150         mActivity.onUsername((v) -> v.setText("malkovich"));
1151         mActivity.onPassword((v) -> v.setText("malkovich"));
1152 
1153         // ...and login
1154         final String expectedMessage = getWelcomeMessage("malkovich");
1155         final String actualMessage = mActivity.tapLogin();
1156         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1157 
1158         // Assert the snack bar is shown and tap "Save".
1159         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1160 
1161         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1162         sReplier.assertNumberUnhandledSaveRequests(0);
1163 
1164         // Assert value of expected fields - should not be sanitized.
1165         try {
1166             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1167             assertTextAndValue(username, "malkovich");
1168             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1169             assertTextAndValue(password, "malkovich");
1170         } catch (AssertionError | RuntimeException e) {
1171             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1172             throw e;
1173         }
1174 
1175         // Sanity check: once saved, the session should be finsihed.
1176         assertNoDanglingSessions();
1177     }
1178 
1179     enum DismissType {
1180         BACK_BUTTON,
1181         HOME_BUTTON,
1182         TOUCH_OUTSIDE
1183     }
1184 
1185     @Test
testSaveGoesAwayWhenTappingHomeButton()1186     public void testSaveGoesAwayWhenTappingHomeButton() throws Exception {
1187         saveGoesAway(DismissType.HOME_BUTTON);
1188     }
1189 
1190     /* TODO: add these when fixed.
1191     @Test
1192     public void testSaveGoesAwayWhenTappingBackButton() throws Exception {
1193         saveGoesAway(DismissType.BACK_BUTTON);
1194     }
1195 
1196     @Test
1197     public void testSaveGoesAwayWhenTouchingOutside() throws Exception {
1198         saveGoesAway(DismissType.TOUCH_OUTSIDE);
1199     }
1200     */
1201 
saveGoesAway(DismissType dismissType)1202     private void saveGoesAway(DismissType dismissType) throws Exception {
1203         enableService();
1204 
1205         // Set expectations.
1206         sReplier.addResponse(new CannedFillResponse.Builder()
1207                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1208                 .build());
1209 
1210         // Trigger auto-fill.
1211         mActivity.onUsername(View::requestFocus);
1212 
1213         // Sanity check.
1214         sUiBot.assertNoDatasets();
1215 
1216         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1217         // the session started
1218         sReplier.getNextFillRequest();
1219 
1220         // Set credentials...
1221         mActivity.onUsername((v) -> v.setText("malkovich"));
1222         mActivity.onPassword((v) -> v.setText("malkovich"));
1223 
1224         // ...and login
1225         final String expectedMessage = getWelcomeMessage("malkovich");
1226         final String actualMessage = mActivity.tapLogin();
1227         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1228 
1229         // Assert the snack bar is shown and tap "Save".
1230         sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
1231 
1232         // Then make sure it goes away when user doesn't want it..
1233         switch (dismissType) {
1234             case BACK_BUTTON:
1235                 sUiBot.pressBack();
1236                 break;
1237             case HOME_BUTTON:
1238                 sUiBot.pressHome();
1239                 break;
1240             case TOUCH_OUTSIDE:
1241                 sUiBot.assertShownByText(expectedMessage).click();
1242                 break;
1243             default:
1244                 throw new IllegalArgumentException("invalid dismiss type: " + dismissType);
1245         }
1246         sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
1247     }
1248 
1249     @Test
testSaveOnlyPreFilled()1250     public void testSaveOnlyPreFilled() throws Exception {
1251         saveOnlyTestPreFilled(false);
1252     }
1253 
1254     @Test
testSaveOnlyTriggeredManuallyPreFilled()1255     public void testSaveOnlyTriggeredManuallyPreFilled() throws Exception {
1256         saveOnlyTestPreFilled(true);
1257     }
1258 
saveOnlyTestPreFilled(boolean manually)1259     private void saveOnlyTestPreFilled(boolean manually) throws Exception {
1260         enableService();
1261 
1262         // Set expectations.
1263         sReplier.addResponse(new CannedFillResponse.Builder()
1264                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1265                 .build());
1266 
1267         // Set activity
1268         mActivity.onUsername((v) -> v.setText("user_before"));
1269         mActivity.onPassword((v) -> v.setText("pass_before"));
1270 
1271         // Trigger auto-fill.
1272         if (manually) {
1273             mActivity.forceAutofillOnUsername();
1274         } else {
1275             mActivity.onUsername(View::requestFocus);
1276         }
1277 
1278         // Sanity check.
1279         sUiBot.assertNoDatasets();
1280 
1281         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1282         // the session started
1283         sReplier.getNextFillRequest();
1284 
1285         // Set credentials...
1286         mActivity.onUsername((v) -> v.setText("user_after"));
1287         mActivity.onPassword((v) -> v.setText("pass_after"));
1288 
1289         // ...and login
1290         final String expectedMessage = getWelcomeMessage("user_after");
1291         final String actualMessage = mActivity.tapLogin();
1292         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1293 
1294         // Assert the snack bar is shown and tap "Save".
1295         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1296 
1297         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1298         sReplier.assertNumberUnhandledSaveRequests(0);
1299 
1300         // Assert value of expected fields - should not be sanitized.
1301         try {
1302             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1303             assertTextAndValue(username, "user_after");
1304             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1305             assertTextAndValue(password, "pass_after");
1306         } catch (AssertionError | RuntimeException e) {
1307             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1308             throw e;
1309         }
1310 
1311         // Sanity check: once saved, the session should be finsihed.
1312         assertNoDanglingSessions();
1313     }
1314 
1315     @Test
testSaveOnlyTwoRequiredFieldsOnePrefilled()1316     public void testSaveOnlyTwoRequiredFieldsOnePrefilled() throws Exception {
1317         enableService();
1318 
1319         // Set expectations.
1320         sReplier.addResponse(new CannedFillResponse.Builder()
1321                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1322                 .build());
1323 
1324         // Set activity
1325         mActivity.onUsername((v) -> v.setText("I_AM_USER"));
1326 
1327         // Trigger auto-fill.
1328         mActivity.onPassword(View::requestFocus);
1329 
1330         // Wait for onFill() before changing value, otherwise the fields might be changed before
1331         // the session started
1332         sReplier.getNextFillRequest();
1333         sUiBot.assertNoDatasets();
1334 
1335         // Set credentials...
1336         mActivity.onPassword((v) -> v.setText("thou should pass")); // contains pass
1337 
1338         // ...and login
1339         final String expectedMessage = getWelcomeMessage("I_AM_USER"); // contains pass
1340         final String actualMessage = mActivity.tapLogin();
1341         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1342 
1343         // Assert the snack bar is shown and tap "Save".
1344         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1345 
1346         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1347         sReplier.assertNumberUnhandledSaveRequests(0);
1348 
1349         // Assert value of expected fields - should not be sanitized.
1350         try {
1351             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1352             assertTextAndValue(username, "I_AM_USER");
1353             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1354             assertTextAndValue(password, "thou should pass");
1355         } catch (AssertionError | RuntimeException e) {
1356             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1357             throw e;
1358         }
1359 
1360         // Sanity check: once saved, the session should be finsihed.
1361         assertNoDanglingSessions();
1362     }
1363 
1364     @Test
testSaveOnlyOptionalField()1365     public void testSaveOnlyOptionalField() throws Exception {
1366         enableService();
1367 
1368         // Set expectations.
1369         sReplier.addResponse(new CannedFillResponse.Builder()
1370                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME)
1371                 .setOptionalSavableIds(ID_PASSWORD)
1372                 .build());
1373 
1374         // Trigger auto-fill.
1375         mActivity.onUsername(View::requestFocus);
1376 
1377         // Sanity check.
1378         sUiBot.assertNoDatasets();
1379 
1380         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1381         // the session started
1382         sReplier.getNextFillRequest();
1383 
1384         // Set credentials...
1385         mActivity.onUsername((v) -> v.setText("malkovich"));
1386         mActivity.onPassword(View::requestFocus);
1387         mActivity.onPassword((v) -> v.setText("malkovich"));
1388 
1389         // ...and login
1390         final String expectedMessage = getWelcomeMessage("malkovich");
1391         final String actualMessage = mActivity.tapLogin();
1392         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1393 
1394         // Assert the snack bar is shown and tap "Save".
1395         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1396 
1397         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1398 
1399         // Assert value of expected fields - should not be sanitized.
1400         final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1401         assertTextAndValue(username, "malkovich");
1402         final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1403         assertTextAndValue(password, "malkovich");
1404 
1405         // Sanity check: once saved, the session should be finsihed.
1406         assertNoDanglingSessions();
1407     }
1408 
1409     @Test
testGenericSave()1410     public void testGenericSave() throws Exception {
1411         customizedSaveTest(SAVE_DATA_TYPE_GENERIC);
1412     }
1413 
1414     @Test
testCustomizedSavePassword()1415     public void testCustomizedSavePassword() throws Exception {
1416         customizedSaveTest(SAVE_DATA_TYPE_PASSWORD);
1417     }
1418 
1419     @Test
testCustomizedSaveAddress()1420     public void testCustomizedSaveAddress() throws Exception {
1421         customizedSaveTest(SAVE_DATA_TYPE_ADDRESS);
1422     }
1423 
1424     @Test
testCustomizedSaveCreditCard()1425     public void testCustomizedSaveCreditCard() throws Exception {
1426         customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD);
1427     }
1428 
1429     @Test
testCustomizedSaveUsername()1430     public void testCustomizedSaveUsername() throws Exception {
1431         customizedSaveTest(SAVE_DATA_TYPE_USERNAME);
1432     }
1433 
1434     @Test
testCustomizedSaveEmailAddress()1435     public void testCustomizedSaveEmailAddress() throws Exception {
1436         customizedSaveTest(SAVE_DATA_TYPE_EMAIL_ADDRESS);
1437     }
1438 
customizedSaveTest(int type)1439     private void customizedSaveTest(int type) throws Exception {
1440         // Set service.
1441         enableService();
1442 
1443         // Set expectations.
1444         final String saveDescription = "Your data will be saved with love and care...";
1445         sReplier.addResponse(new CannedFillResponse.Builder()
1446                 .setRequiredSavableIds(type, ID_USERNAME, ID_PASSWORD)
1447                 .setSaveDescription(saveDescription)
1448                 .build());
1449 
1450         // Trigger auto-fill.
1451         mActivity.onUsername(View::requestFocus);
1452 
1453         // Sanity check.
1454         sUiBot.assertNoDatasets();
1455 
1456         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1457         // the session started.
1458         sReplier.getNextFillRequest();
1459 
1460         // Set credentials...
1461         mActivity.onUsername((v) -> v.setText("malkovich"));
1462         mActivity.onPassword((v) -> v.setText("malkovich"));
1463 
1464         // ...and login
1465         final String expectedMessage = getWelcomeMessage("malkovich");
1466         final String actualMessage = mActivity.tapLogin();
1467         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1468 
1469         // Assert the snack bar is shown and tap "Save".
1470         final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(saveDescription, type);
1471         sUiBot.saveForAutofill(saveSnackBar, true);
1472 
1473         // Assert save was called.
1474         sReplier.getNextSaveRequest();
1475     }
1476 
1477     @Test
testAutoFillOneDatasetAndSaveWhenFlagSecure()1478     public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception {
1479         mActivity.setFlags(FLAG_SECURE);
1480         testAutoFillOneDatasetAndSave();
1481     }
1482 
1483     @Test
testAutoFillOneDatasetWhenFlagSecure()1484     public void testAutoFillOneDatasetWhenFlagSecure() throws Exception {
1485         mActivity.setFlags(FLAG_SECURE);
1486         testAutoFillOneDataset();
1487     }
1488 
1489     @Test
testFillResponseAuthBothFields()1490     public void testFillResponseAuthBothFields() throws Exception {
1491         fillResponseAuthBothFields(false);
1492     }
1493 
1494     @Test
testFillResponseAuthBothFieldsUserCancelsFirstAttempt()1495     public void testFillResponseAuthBothFieldsUserCancelsFirstAttempt() throws Exception {
1496         fillResponseAuthBothFields(true);
1497     }
1498 
fillResponseAuthBothFields(boolean cancelFirstAttempt)1499     private void fillResponseAuthBothFields(boolean cancelFirstAttempt) throws Exception {
1500         // Set service.
1501         enableService();
1502         final MyAutofillCallback callback = mActivity.registerCallback();
1503 
1504         // Prepare the authenticated response
1505         final Bundle clientState = new Bundle();
1506         clientState.putString("numbers", "4815162342");
1507         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1508                 new CannedFillResponse.Builder().addDataset(
1509                         new CannedDataset.Builder()
1510                                 .setField(ID_USERNAME, "dude")
1511                                 .setField(ID_PASSWORD, "sweet")
1512                                 .setId("name")
1513                                 .setPresentation(createPresentation("Dataset"))
1514                                 .build())
1515                         .setExtras(clientState).build());
1516 
1517         // Configure the service behavior
1518         sReplier.addResponse(new CannedFillResponse.Builder()
1519                 .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
1520                 .setPresentation(createPresentation("Tap to auth response"))
1521                 .setExtras(clientState)
1522                 .build());
1523 
1524         // Set expectation for the activity
1525         mActivity.expectAutoFill("dude", "sweet");
1526 
1527         // Trigger auto-fill.
1528         mActivity.onUsername(View::requestFocus);
1529 
1530         // Wait for onFill() before proceeding.
1531         sReplier.getNextFillRequest();
1532         final View username = mActivity.getUsername();
1533         callback.assertUiShownEvent(username);
1534         sUiBot.assertDatasets("Tap to auth response");
1535 
1536         // Make sure UI is show on 2nd field as well
1537         final View password = mActivity.getPassword();
1538         mActivity.onPassword(View::requestFocus);
1539         callback.assertUiHiddenEvent(username);
1540         callback.assertUiShownEvent(password);
1541         sUiBot.assertDatasets("Tap to auth response");
1542 
1543         // Now tap on 1st field to show it again...
1544         mActivity.onUsername(View::requestFocus);
1545         callback.assertUiHiddenEvent(password);
1546         callback.assertUiShownEvent(username);
1547 
1548         if (cancelFirstAttempt) {
1549             // Trigger the auth dialog, but emulate cancel.
1550             AuthenticationActivity.setResultCode(RESULT_CANCELED);
1551             sUiBot.selectDataset("Tap to auth response");
1552             callback.assertUiHiddenEvent(username);
1553             callback.assertUiShownEvent(username);
1554             sUiBot.assertDatasets("Tap to auth response");
1555 
1556             // Make sure it's still shown on other fields...
1557             mActivity.onPassword(View::requestFocus);
1558             callback.assertUiHiddenEvent(username);
1559             callback.assertUiShownEvent(password);
1560             sUiBot.assertDatasets("Tap to auth response");
1561 
1562             // Tap on 1st field to show it again...
1563             mActivity.onUsername(View::requestFocus);
1564             callback.assertUiHiddenEvent(password);
1565             callback.assertUiShownEvent(username);
1566         }
1567 
1568         // ...and select it this time
1569         AuthenticationActivity.setResultCode(RESULT_OK);
1570         sUiBot.selectDataset("Tap to auth response");
1571         callback.assertUiHiddenEvent(username);
1572         callback.assertUiShownEvent(username);
1573         final UiObject2 picker = sUiBot.assertDatasets("Dataset");
1574         sUiBot.selectDataset(picker, "Dataset");
1575         callback.assertUiHiddenEvent(username);
1576         sUiBot.assertNoDatasets();
1577 
1578         // Check the results.
1579         mActivity.assertAutoFilled();
1580 
1581         final Bundle data = AuthenticationActivity.getData();
1582         assertThat(data).isNotNull();
1583         final String extraValue = data.getString("numbers");
1584         assertThat(extraValue).isEqualTo("4815162342");
1585     }
1586 
1587     @Test
testFillResponseAuthJustOneField()1588     public void testFillResponseAuthJustOneField() throws Exception {
1589         // Set service.
1590         enableService();
1591         final MyAutofillCallback callback = mActivity.registerCallback();
1592 
1593         // Prepare the authenticated response
1594         final Bundle clientState = new Bundle();
1595         clientState.putString("numbers", "4815162342");
1596         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1597                 new CannedFillResponse.Builder().addDataset(
1598                         new CannedDataset.Builder()
1599                                 .setField(ID_USERNAME, "dude")
1600                                 .setField(ID_PASSWORD, "sweet")
1601                                 .setPresentation(createPresentation("Dataset"))
1602                                 .build())
1603                         .build());
1604 
1605         // Configure the service behavior
1606         sReplier.addResponse(new CannedFillResponse.Builder()
1607                 .setAuthentication(authentication, ID_USERNAME)
1608                 .setIgnoreFields(ID_PASSWORD)
1609                 .setPresentation(createPresentation("Tap to auth response"))
1610                 .setExtras(clientState)
1611                 .build());
1612 
1613         // Set expectation for the activity
1614         mActivity.expectAutoFill("dude", "sweet");
1615 
1616         // Trigger auto-fill.
1617         mActivity.onUsername(View::requestFocus);
1618 
1619         // Wait for onFill() before proceeding.
1620         sReplier.getNextFillRequest();
1621         final View username = mActivity.getUsername();
1622         callback.assertUiShownEvent(username);
1623         sUiBot.assertDatasets("Tap to auth response");
1624 
1625         // Make sure UI is not show on 2nd field
1626         mActivity.onPassword(View::requestFocus);
1627         callback.assertUiHiddenEvent(username);
1628         sUiBot.assertNoDatasets();
1629         // Now tap on 1st field to show it again...
1630         mActivity.onUsername(View::requestFocus);
1631         callback.assertUiShownEvent(username);
1632 
1633         // ...and select it this time
1634         sUiBot.selectDataset("Tap to auth response");
1635         callback.assertUiHiddenEvent(username);
1636         final UiObject2 picker = sUiBot.assertDatasets("Dataset");
1637 
1638         callback.assertUiShownEvent(username);
1639         sUiBot.selectDataset(picker, "Dataset");
1640         callback.assertUiHiddenEvent(username);
1641         sUiBot.assertNoDatasets();
1642 
1643         // Check the results.
1644         mActivity.assertAutoFilled();
1645         final Bundle data = AuthenticationActivity.getData();
1646         assertThat(data).isNotNull();
1647         final String extraValue = data.getString("numbers");
1648         assertThat(extraValue).isEqualTo("4815162342");
1649     }
1650 
1651     @Test
testFillResponseAuthServiceHasNoData()1652     public void testFillResponseAuthServiceHasNoData() throws Exception {
1653         // Set service.
1654         enableService();
1655         final MyAutofillCallback callback = mActivity.registerCallback();
1656 
1657         // Prepare the authenticated response
1658         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1659                 new CannedFillResponse.Builder()
1660                         .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1661                         .build());
1662 
1663         // Configure the service behavior
1664         sReplier.addResponse(new CannedFillResponse.Builder()
1665                 .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
1666                 .setPresentation(createPresentation("Tap to auth response"))
1667                 .build());
1668 
1669         // Trigger auto-fill.
1670         mActivity.onUsername(View::requestFocus);
1671 
1672         // Wait for onFill() before proceeding.
1673         sReplier.getNextFillRequest();
1674         final View username = mActivity.getUsername();
1675         callback.assertUiShownEvent(username);
1676 
1677         // Select the authentication dialog.
1678         sUiBot.selectDataset("Tap to auth response");
1679         callback.assertUiHiddenEvent(username);
1680         sUiBot.assertNoDatasets();
1681     }
1682 
1683     @Test
testDatasetAuthTwoFields()1684     public void testDatasetAuthTwoFields() throws Exception {
1685         datasetAuthTwoFields(false);
1686     }
1687 
1688     @Test
testDatasetAuthTwoFieldsUserCancelsFirstAttempt()1689     public void testDatasetAuthTwoFieldsUserCancelsFirstAttempt() throws Exception {
1690         datasetAuthTwoFields(true);
1691     }
1692 
datasetAuthTwoFields(boolean cancelFirstAttempt)1693     private void datasetAuthTwoFields(boolean cancelFirstAttempt) throws Exception {
1694         // TODO: current API requires these fields...
1695         final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
1696         final String bogusValue = "Y U REQUIRE IT?";
1697 
1698         // Set service.
1699         enableService();
1700         final MyAutofillCallback callback = mActivity.registerCallback();
1701 
1702         // Prepare the authenticated response
1703         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1704                 new CannedDataset.Builder()
1705                         .setField(ID_USERNAME, "dude")
1706                         .setField(ID_PASSWORD, "sweet")
1707                         .setPresentation(bogusPresentation)
1708                         .build());
1709 
1710         // Configure the service behavior
1711         sReplier.addResponse(new CannedFillResponse.Builder()
1712                 .addDataset(new CannedDataset.Builder()
1713                         .setField(ID_USERNAME, bogusValue)
1714                         .setField(ID_PASSWORD, bogusValue)
1715                         .setPresentation(createPresentation("Tap to auth dataset"))
1716                         .setAuthentication(authentication)
1717                         .build())
1718                 .build());
1719 
1720         // Set expectation for the activity
1721         mActivity.expectAutoFill("dude", "sweet");
1722 
1723         // Trigger auto-fill.
1724         mActivity.onUsername(View::requestFocus);
1725 
1726         // Wait for onFill() before proceeding.
1727         sReplier.getNextFillRequest();
1728         final View username = mActivity.getUsername();
1729         callback.assertUiShownEvent(username);
1730         sUiBot.assertDatasets("Tap to auth dataset");
1731 
1732         // Make sure UI is show on 2nd field as well
1733         final View password = mActivity.getPassword();
1734         mActivity.onPassword(View::requestFocus);
1735         callback.assertUiHiddenEvent(username);
1736         callback.assertUiShownEvent(password);
1737         sUiBot.assertDatasets("Tap to auth dataset");
1738 
1739         // Now tap on 1st field to show it again...
1740         mActivity.onUsername(View::requestFocus);
1741         callback.assertUiHiddenEvent(password);
1742         callback.assertUiShownEvent(username);
1743         sUiBot.assertDatasets("Tap to auth dataset");
1744 
1745         if (cancelFirstAttempt) {
1746             // Trigger the auth dialog, but emulate cancel.
1747             AuthenticationActivity.setResultCode(RESULT_CANCELED);
1748             sUiBot.selectDataset("Tap to auth dataset");
1749             callback.assertUiHiddenEvent(username);
1750             callback.assertUiShownEvent(username);
1751             sUiBot.assertDatasets("Tap to auth dataset");
1752 
1753             // Make sure it's still shown on other fields...
1754             mActivity.onPassword(View::requestFocus);
1755             callback.assertUiHiddenEvent(username);
1756             callback.assertUiShownEvent(password);
1757             sUiBot.assertDatasets("Tap to auth dataset");
1758 
1759             // Tap on 1st field to show it again...
1760             mActivity.onUsername(View::requestFocus);
1761             callback.assertUiHiddenEvent(password);
1762             callback.assertUiShownEvent(username);
1763         }
1764 
1765         // ...and select it this time
1766         AuthenticationActivity.setResultCode(RESULT_OK);
1767         sUiBot.selectDataset("Tap to auth dataset");
1768         callback.assertUiHiddenEvent(username);
1769         sUiBot.assertNoDatasets();
1770 
1771         // Check the results.
1772         mActivity.assertAutoFilled();
1773     }
1774 
1775     @Test
testDatasetAuthTwoFieldsReplaceResponse()1776     public void testDatasetAuthTwoFieldsReplaceResponse() throws Exception {
1777         // Set service.
1778         enableService();
1779         final MyAutofillCallback callback = mActivity.registerCallback();
1780 
1781         // Prepare the authenticated response
1782         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1783                 new CannedFillResponse.Builder().addDataset(
1784                         new CannedDataset.Builder()
1785                                 .setField(ID_USERNAME, "dude")
1786                                 .setField(ID_PASSWORD, "sweet")
1787                                 .setPresentation(createPresentation("Dataset"))
1788                                 .build())
1789                         .build());
1790 
1791         // Set up the authentication response client state
1792         final Bundle authentionClientState = new Bundle();
1793         authentionClientState.putCharSequence("clientStateKey1", "clientStateValue1");
1794 
1795         // Configure the service behavior
1796         sReplier.addResponse(new CannedFillResponse.Builder()
1797                 .addDataset(new CannedDataset.Builder()
1798                         .setField(ID_USERNAME, (AutofillValue) null)
1799                         .setField(ID_PASSWORD, (AutofillValue) null)
1800                         .setPresentation(createPresentation("Tap to auth dataset"))
1801                         .setAuthentication(authentication)
1802                         .build())
1803                 .setExtras(authentionClientState)
1804                 .build());
1805 
1806         // Set expectation for the activity
1807         mActivity.expectAutoFill("dude", "sweet");
1808 
1809         // Trigger auto-fill.
1810         mActivity.onUsername(View::requestFocus);
1811 
1812         // Wait for onFill() before proceeding.
1813         sReplier.getNextFillRequest();
1814         final View username = mActivity.getUsername();
1815 
1816         // Authenticate
1817         callback.assertUiShownEvent(username);
1818         sUiBot.selectDataset("Tap to auth dataset");
1819         callback.assertUiHiddenEvent(username);
1820 
1821         // Select a dataset from the new response
1822         callback.assertUiShownEvent(username);
1823         sUiBot.selectDataset("Dataset");
1824         callback.assertUiHiddenEvent(username);
1825         sUiBot.assertNoDatasets();
1826 
1827         // Check the results.
1828         mActivity.assertAutoFilled();
1829 
1830         final Bundle data = AuthenticationActivity.getData();
1831         assertThat(data).isNotNull();
1832         final String extraValue = data.getString("clientStateKey1");
1833         assertThat(extraValue).isEqualTo("clientStateValue1");
1834     }
1835 
1836     @Test
testDatasetAuthTwoFieldsNoValues()1837     public void testDatasetAuthTwoFieldsNoValues() throws Exception {
1838         // Set service.
1839         enableService();
1840         final MyAutofillCallback callback = mActivity.registerCallback();
1841 
1842         // Create the authentication intent
1843         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1844                 new CannedDataset.Builder()
1845                         .setField(ID_USERNAME, "dude")
1846                         .setField(ID_PASSWORD, "sweet")
1847                         .setPresentation(createPresentation("Dataset"))
1848                         .build());
1849 
1850         // Configure the service behavior
1851         sReplier.addResponse(new CannedFillResponse.Builder()
1852                 .addDataset(new CannedDataset.Builder()
1853                         .setField(ID_USERNAME, (String) null)
1854                         .setField(ID_PASSWORD, (String) null)
1855                         .setPresentation(createPresentation("Tap to auth dataset"))
1856                         .setAuthentication(authentication)
1857                         .build())
1858                 .build());
1859 
1860         // Set expectation for the activity
1861         mActivity.expectAutoFill("dude", "sweet");
1862 
1863         // Trigger auto-fill.
1864         mActivity.onUsername(View::requestFocus);
1865 
1866         // Wait for onFill() before proceeding.
1867         sReplier.getNextFillRequest();
1868         final View username = mActivity.getUsername();
1869 
1870         // Authenticate
1871         callback.assertUiShownEvent(username);
1872         sUiBot.selectDataset("Tap to auth dataset");
1873         callback.assertUiHiddenEvent(username);
1874         sUiBot.assertNoDatasets();
1875 
1876         // Check the results.
1877         mActivity.assertAutoFilled();
1878     }
1879 
1880     @Test
testDatasetAuthTwoDatasets()1881     public void testDatasetAuthTwoDatasets() throws Exception {
1882         // TODO: current API requires these fields...
1883         final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
1884         final String bogusValue = "Y U REQUIRE IT?";
1885 
1886         // Set service.
1887         enableService();
1888         final MyAutofillCallback callback = mActivity.registerCallback();
1889 
1890         // Create the authentication intents
1891         final CannedDataset unlockedDataset = new CannedDataset.Builder()
1892                 .setField(ID_USERNAME, "dude")
1893                 .setField(ID_PASSWORD, "sweet")
1894                 .setPresentation(bogusPresentation)
1895                 .build();
1896         final IntentSender authentication1 = AuthenticationActivity.createSender(getContext(), 1,
1897                 unlockedDataset);
1898         final IntentSender authentication2 = AuthenticationActivity.createSender(getContext(), 2,
1899                 unlockedDataset);
1900 
1901         // Configure the service behavior
1902         sReplier.addResponse(new CannedFillResponse.Builder()
1903                 .addDataset(new CannedDataset.Builder()
1904                         .setField(ID_USERNAME, bogusValue)
1905                         .setField(ID_PASSWORD, bogusValue)
1906                         .setPresentation(createPresentation("Tap to auth dataset 1"))
1907                         .setAuthentication(authentication1)
1908                         .build())
1909                 .addDataset(new CannedDataset.Builder()
1910                         .setField(ID_USERNAME, bogusValue)
1911                         .setField(ID_PASSWORD, bogusValue)
1912                         .setPresentation(createPresentation("Tap to auth dataset 2"))
1913                         .setAuthentication(authentication2)
1914                         .build())
1915                 .build());
1916 
1917         // Set expectation for the activity
1918         mActivity.expectAutoFill("dude", "sweet");
1919 
1920         // Trigger auto-fill.
1921         mActivity.onUsername(View::requestFocus);
1922 
1923         // Wait for onFill() before proceeding.
1924         sReplier.getNextFillRequest();
1925         final View username = mActivity.getUsername();
1926 
1927         // Authenticate
1928         callback.assertUiShownEvent(username);
1929         sUiBot.assertDatasets("Tap to auth dataset 1", "Tap to auth dataset 2");
1930 
1931         sUiBot.selectDataset("Tap to auth dataset 1");
1932         callback.assertUiHiddenEvent(username);
1933         sUiBot.assertNoDatasets();
1934 
1935         // Check the results.
1936         mActivity.assertAutoFilled();
1937     }
1938 
1939     @Test
testDatasetAuthMixedSelectAuth()1940     public void testDatasetAuthMixedSelectAuth() throws Exception {
1941         datasetAuthMixedTest(true);
1942     }
1943 
1944     @Test
testDatasetAuthMixedSelectNonAuth()1945     public void testDatasetAuthMixedSelectNonAuth() throws Exception {
1946         datasetAuthMixedTest(false);
1947     }
1948 
datasetAuthMixedTest(boolean selectAuth)1949     private void datasetAuthMixedTest(boolean selectAuth) throws Exception {
1950         // Set service.
1951         enableService();
1952         final MyAutofillCallback callback = mActivity.registerCallback();
1953 
1954         // Prepare the authenticated response
1955         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
1956                 new CannedDataset.Builder()
1957                         .setField(ID_USERNAME, "dude")
1958                         .setField(ID_PASSWORD, "sweet")
1959                         .setPresentation(createPresentation("Dataset"))
1960                         .build());
1961 
1962         // Configure the service behavior
1963         sReplier.addResponse(new CannedFillResponse.Builder()
1964                 .addDataset(new CannedDataset.Builder()
1965                         .setField(ID_USERNAME, "dude")
1966                         .setField(ID_PASSWORD, "sweet")
1967                         .setPresentation(createPresentation("Tap to auth dataset"))
1968                         .setAuthentication(authentication)
1969                         .build())
1970                 .addDataset(new CannedDataset.Builder()
1971                         .setField(ID_USERNAME, "DUDE")
1972                         .setField(ID_PASSWORD, "SWEET")
1973                         .setPresentation(createPresentation("What, me auth?"))
1974                         .build())
1975                 .build());
1976 
1977         // Set expectation for the activity
1978         if (selectAuth) {
1979             mActivity.expectAutoFill("dude", "sweet");
1980         } else {
1981             mActivity.expectAutoFill("DUDE", "SWEET");
1982         }
1983 
1984         // Trigger auto-fill.
1985         mActivity.onUsername(View::requestFocus);
1986 
1987         // Wait for onFill() before proceeding.
1988         sReplier.getNextFillRequest();
1989         final View username = mActivity.getUsername();
1990 
1991         // Authenticate
1992         callback.assertUiShownEvent(username);
1993         sUiBot.assertDatasets("Tap to auth dataset", "What, me auth?");
1994 
1995         final String chosenOne = selectAuth ? "Tap to auth dataset" : "What, me auth?";
1996         sUiBot.selectDataset(chosenOne);
1997         callback.assertUiHiddenEvent(username);
1998         sUiBot.assertNoDatasets();
1999 
2000         // Check the results.
2001         mActivity.assertAutoFilled();
2002     }
2003 
2004     @Test
testDisableSelf()2005     public void testDisableSelf() throws Exception {
2006         enableService();
2007 
2008         // Can disable while connected.
2009         mActivity.runOnUiThread(() -> getContext().getSystemService(
2010                 AutofillManager.class).disableAutofillServices());
2011 
2012         // Ensure disabled.
2013         assertServiceDisabled();
2014     }
2015 
2016     @Test
testRejectStyleNegativeSaveButton()2017     public void testRejectStyleNegativeSaveButton() throws Exception {
2018         enableService();
2019 
2020         // Set service behavior.
2021 
2022         final String intentAction = "android.autofillservice.cts.CUSTOM_ACTION";
2023 
2024         // Configure the save UI.
2025         final IntentSender listener = PendingIntent.getBroadcast(
2026                 getContext(), 0, new Intent(intentAction), 0).getIntentSender();
2027 
2028         sReplier.addResponse(new CannedFillResponse.Builder()
2029                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2030                 .setNegativeAction(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT, listener)
2031                 .build());
2032 
2033         // Trigger auto-fill.
2034         mActivity.onUsername(View::requestFocus);
2035 
2036         // Wait for onFill() before proceeding.
2037         sReplier.getNextFillRequest();
2038 
2039         // Trigger save.
2040         mActivity.onUsername((v) -> v.setText("foo"));
2041         mActivity.onPassword((v) -> v.setText("foo"));
2042         mActivity.tapLogin();
2043 
2044         // Start watching for the negative intent
2045         final CountDownLatch latch = new CountDownLatch(1);
2046         final IntentFilter intentFilter = new IntentFilter(intentAction);
2047         getContext().registerReceiver(new BroadcastReceiver() {
2048             @Override
2049             public void onReceive(Context context, Intent intent) {
2050                 getContext().unregisterReceiver(this);
2051                 latch.countDown();
2052             }
2053         }, intentFilter);
2054 
2055         // Trigger the negative button.
2056         sUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT,
2057                 false, SAVE_DATA_TYPE_PASSWORD);
2058 
2059         // Wait for the custom action.
2060         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
2061 
2062         assertNoDanglingSessions();
2063     }
2064 
2065     @Test
testCancelStyleNegativeSaveButton()2066     public void testCancelStyleNegativeSaveButton() throws Exception {
2067         enableService();
2068 
2069         // Set service behavior.
2070 
2071         final String intentAction = "android.autofillservice.cts.CUSTOM_ACTION";
2072 
2073         // Configure the save UI.
2074         final IntentSender listener = PendingIntent.getBroadcast(
2075                 getContext(), 0, new Intent(intentAction), 0).getIntentSender();
2076 
2077         sReplier.addResponse(new CannedFillResponse.Builder()
2078                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2079                 .setNegativeAction(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL, listener)
2080                 .build());
2081 
2082         // Trigger auto-fill.
2083         mActivity.onUsername(View::requestFocus);
2084 
2085         // Wait for onFill() before proceeding.
2086         sReplier.getNextFillRequest();
2087 
2088         // Trigger save.
2089         mActivity.onUsername((v) -> v.setText("foo"));
2090         mActivity.onPassword((v) -> v.setText("foo"));
2091         mActivity.tapLogin();
2092 
2093         // Start watching for the negative intent
2094         final CountDownLatch latch = new CountDownLatch(1);
2095         final IntentFilter intentFilter = new IntentFilter(intentAction);
2096         getContext().registerReceiver(new BroadcastReceiver() {
2097             @Override
2098             public void onReceive(Context context, Intent intent) {
2099                 getContext().unregisterReceiver(this);
2100                 latch.countDown();
2101             }
2102         }, intentFilter);
2103 
2104         // Trigger the negative button.
2105         sUiBot.saveForAutofill(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL,
2106                 false, SAVE_DATA_TYPE_PASSWORD);
2107 
2108         // Wait for the custom action.
2109         assertThat(latch.await(500, TimeUnit.SECONDS)).isTrue();
2110 
2111         assertNoDanglingSessions();
2112     }
2113 
2114     @Test
testGetTextInputType()2115     public void testGetTextInputType() throws Exception {
2116         // Set service.
2117         enableService();
2118 
2119         // Set expectations.
2120         sReplier.addResponse(NO_RESPONSE);
2121 
2122         // Trigger auto-fill.
2123         mActivity.onUsername(View::requestFocus);
2124 
2125         // Assert input text on fill request:
2126         final FillRequest fillRequest = sReplier.getNextFillRequest();
2127 
2128         final ViewNode label = findNodeByResourceId(fillRequest.structure, ID_PASSWORD_LABEL);
2129         assertThat(label.getInputType()).isEqualTo(TYPE_NULL);
2130         final ViewNode password = findNodeByResourceId(fillRequest.structure, ID_PASSWORD);
2131         assertWithMessage("No TYPE_TEXT_VARIATION_PASSWORD on %s", password.getInputType())
2132                 .that(password.getInputType() & TYPE_TEXT_VARIATION_PASSWORD)
2133                 .isEqualTo(TYPE_TEXT_VARIATION_PASSWORD);
2134     }
2135 
2136     @Test
testNoContainers()2137     public void testNoContainers() throws Exception {
2138         // Set service.
2139         enableService();
2140 
2141         // Set expectations.
2142         sReplier.addResponse(NO_RESPONSE);
2143 
2144         // Trigger auto-fill.
2145         mActivity.onUsername(View::requestFocus);
2146 
2147         sUiBot.assertNoDatasets();
2148 
2149         final FillRequest fillRequest = sReplier.getNextFillRequest();
2150 
2151         // Assert it only has 1 root view with 10 "leaf" nodes:
2152         // 1.text view for app title
2153         // 2.username text label
2154         // 3.username text field
2155         // 4.password text label
2156         // 5.password text field
2157         // 6.output text field
2158         // 7.clear button
2159         // 8.save button
2160         // 9.login button
2161         // 10.cancel button
2162         //
2163         // But it also has an intermediate container (for username) that should be included because
2164         // it has a resource id.
2165 
2166         assertNumberOfChildren(fillRequest.structure, 12);
2167 
2168         // Make sure container with a resource id was included:
2169         final ViewNode usernameContainer = findNodeByResourceId(fillRequest.structure,
2170                 ID_USERNAME_CONTAINER);
2171         assertThat(usernameContainer).isNotNull();
2172         assertThat(usernameContainer.getChildCount()).isEqualTo(2);
2173     }
2174 
2175     @Test
testAutofillManuallyOneDataset()2176     public void testAutofillManuallyOneDataset() throws Exception {
2177         // Set service.
2178         enableService();
2179 
2180         // And activity.
2181         mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
2182 
2183         // Set expectations.
2184         sReplier.addResponse(new CannedDataset.Builder()
2185                 .setField(ID_USERNAME, "dude")
2186                 .setField(ID_PASSWORD, "sweet")
2187                 .setPresentation(createPresentation("The Dude"))
2188                 .build());
2189         mActivity.expectAutoFill("dude", "sweet");
2190 
2191         // Explicitly uses the contextual menu to test that functionality.
2192         sUiBot.getAutofillMenuOption(ID_USERNAME).click();
2193 
2194         final FillRequest fillRequest = sReplier.getNextFillRequest();
2195         assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2196 
2197         // Should have been automatically filled.
2198         sUiBot.selectDataset("The Dude");
2199 
2200         // Check the results.
2201         mActivity.assertAutoFilled();
2202     }
2203 
2204     @Test
testAutofillManuallyTwoDatasetsPickFirst()2205     public void testAutofillManuallyTwoDatasetsPickFirst() throws Exception {
2206         autofillManuallyTwoDatasets(true);
2207     }
2208 
2209     @Test
testAutofillManuallyTwoDatasetsPickSecond()2210     public void testAutofillManuallyTwoDatasetsPickSecond() throws Exception {
2211         autofillManuallyTwoDatasets(false);
2212     }
2213 
autofillManuallyTwoDatasets(boolean pickFirst)2214     private void autofillManuallyTwoDatasets(boolean pickFirst) throws Exception {
2215         // Set service.
2216         enableService();
2217 
2218         // Set expectations.
2219         sReplier.addResponse(new CannedFillResponse.Builder()
2220                 .addDataset(new CannedDataset.Builder()
2221                         .setField(ID_USERNAME, "dude")
2222                         .setField(ID_PASSWORD, "sweet")
2223                         .setPresentation(createPresentation("The Dude"))
2224                         .build())
2225                 .addDataset(new CannedDataset.Builder()
2226                         .setField(ID_USERNAME, "jenny")
2227                         .setField(ID_PASSWORD, "8675309")
2228                         .setPresentation(createPresentation("Jenny"))
2229                         .build())
2230                 .build());
2231         if (pickFirst) {
2232             mActivity.expectAutoFill("dude", "sweet");
2233         } else {
2234             mActivity.expectAutoFill("jenny", "8675309");
2235 
2236         }
2237 
2238         // Force a manual autofill request.
2239         mActivity.forceAutofillOnUsername();
2240 
2241         final FillRequest fillRequest = sReplier.getNextFillRequest();
2242         assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2243 
2244         // Auto-fill it.
2245         final UiObject2 picker = sUiBot.assertDatasets("The Dude", "Jenny");
2246         sUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny");
2247 
2248         // Check the results.
2249         mActivity.assertAutoFilled();
2250     }
2251 
2252     @Test
testAutofillManuallyPartialField()2253     public void testAutofillManuallyPartialField() throws Exception {
2254         // Set service.
2255         enableService();
2256 
2257         // And activity.
2258         mActivity.onUsername((v) -> v.setText("dud"));
2259         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2260 
2261         // Set expectations.
2262         sReplier.addResponse(new CannedDataset.Builder()
2263                 .setField(ID_USERNAME, "dude")
2264                 .setField(ID_PASSWORD, "sweet")
2265                 .setPresentation(createPresentation("The Dude"))
2266                 .build());
2267         mActivity.expectAutoFill("dude", "sweet");
2268 
2269         // Force a manual autofill request.
2270         mActivity.forceAutofillOnUsername();
2271 
2272         final FillRequest fillRequest = sReplier.getNextFillRequest();
2273         assertThat(fillRequest.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2274         // Username value should be available because it triggered the manual request...
2275         assertValue(fillRequest.structure, ID_USERNAME, "dud");
2276         // ... but password didn't
2277         assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
2278 
2279         // Selects the dataset.
2280         sUiBot.selectDataset("The Dude");
2281 
2282         // Check the results.
2283         mActivity.assertAutoFilled();
2284     }
2285 
2286     @Test
testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore()2287     public void testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore() throws Exception {
2288         // Set service.
2289         enableService();
2290 
2291         /*
2292          * 1st fill (automatic).
2293          */
2294         // Set expectations.
2295         sReplier.addResponse(new CannedDataset.Builder()
2296                 .setField(ID_USERNAME, "dude")
2297                 .setField(ID_PASSWORD, "sweet")
2298                 .setPresentation(createPresentation("The Dude"))
2299                 .build());
2300         mActivity.expectAutoFill("dude", "sweet");
2301 
2302         // Trigger auto-fill.
2303         mActivity.onUsername(View::requestFocus);
2304 
2305         // Assert request.
2306         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
2307         assertThat(fillRequest1.flags).isEqualTo(0);
2308         assertTextIsSanitized(fillRequest1.structure, ID_USERNAME);
2309         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
2310 
2311         // Select it.
2312         sUiBot.selectDataset("The Dude");
2313 
2314         // Check the results.
2315         mActivity.assertAutoFilled();
2316 
2317         /*
2318          * 2nd fill (manual).
2319          */
2320         // Set expectations.
2321         sReplier.addResponse(new CannedDataset.Builder()
2322                 .setField(ID_USERNAME, "DUDE")
2323                 .setField(ID_PASSWORD, "SWEET")
2324                 .setPresentation(createPresentation("THE DUDE"))
2325                 .build());
2326         mActivity.expectAutoFill("DUDE", "SWEET");
2327         // Change password to make sure it's not sent to the service.
2328         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2329 
2330         // Trigger auto-fill.
2331         mActivity.forceAutofillOnUsername();
2332 
2333         // Assert request.
2334         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
2335         assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2336         assertValue(fillRequest2.structure, ID_USERNAME, "dude");
2337         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
2338 
2339         // Select it.
2340         sUiBot.selectDataset("THE DUDE");
2341 
2342         // Check the results.
2343         mActivity.assertAutoFilled();
2344     }
2345 
2346     @Test
testAutofillManuallyAgainAfterManuallyAutofilledBefore()2347     public void testAutofillManuallyAgainAfterManuallyAutofilledBefore() throws Exception {
2348         // Set service.
2349         enableService();
2350 
2351         /*
2352          * 1st fill (manual).
2353          */
2354         // Set expectations.
2355         sReplier.addResponse(new CannedDataset.Builder()
2356                 .setField(ID_USERNAME, "dude")
2357                 .setField(ID_PASSWORD, "sweet")
2358                 .setPresentation(createPresentation("The Dude"))
2359                 .build());
2360         mActivity.expectAutoFill("dude", "sweet");
2361 
2362         // Trigger auto-fill.
2363         mActivity.forceAutofillOnUsername();
2364 
2365         // Assert request.
2366         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
2367         assertThat(fillRequest1.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2368         assertValue(fillRequest1.structure, ID_USERNAME, "");
2369         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
2370 
2371         // Select it.
2372         sUiBot.selectDataset("The Dude");
2373 
2374         // Check the results.
2375         mActivity.assertAutoFilled();
2376 
2377         /*
2378          * 2nd fill (manual).
2379          */
2380         // Set expectations.
2381         sReplier.addResponse(new CannedDataset.Builder()
2382                 .setField(ID_USERNAME, "DUDE")
2383                 .setField(ID_PASSWORD, "SWEET")
2384                 .setPresentation(createPresentation("THE DUDE"))
2385                 .build());
2386         mActivity.expectAutoFill("DUDE", "SWEET");
2387         // Change password to make sure it's not sent to the service.
2388         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2389 
2390         // Trigger auto-fill.
2391         mActivity.forceAutofillOnUsername();
2392 
2393         // Assert request.
2394         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
2395         assertThat(fillRequest2.flags).isEqualTo(FLAG_MANUAL_REQUEST);
2396         assertValue(fillRequest2.structure, ID_USERNAME, "dude");
2397         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
2398 
2399         // Select it.
2400         sUiBot.selectDataset("THE DUDE");
2401 
2402         // Check the results.
2403         mActivity.assertAutoFilled();
2404     }
2405 
2406     @Test
testCommitMultipleTimes()2407     public void testCommitMultipleTimes() throws Throwable {
2408         // Set service.
2409         enableService();
2410 
2411         final CannedFillResponse response = new CannedFillResponse.Builder()
2412                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2413                 .build();
2414 
2415         for (int i = 1; i <= 3; i++) {
2416             final String username = "user-" + i;
2417             final String password = "pass-" + i;
2418             try {
2419                 // Set expectations.
2420                 sReplier.addResponse(response);
2421 
2422                 // Trigger auto-fill.
2423                 mActivity.onUsername(View::requestFocus);
2424 
2425                 // Sanity check.
2426                 sUiBot.assertNoDatasets();
2427 
2428                 // Wait for onFill() before proceeding, otherwise the fields might be changed before
2429                 // the session started
2430                 waitUntilConnected();
2431                 sReplier.getNextFillRequest();
2432 
2433                 // Set credentials...
2434                 mActivity.onUsername((v) -> v.setText(username));
2435                 mActivity.onPassword((v) -> v.setText(password));
2436 
2437                 // Change focus to prepare for next step - must do it before session is gone
2438                 mActivity.onPassword(View::requestFocus);
2439 
2440                 // ...and save them
2441                 mActivity.tapSave();
2442 
2443                 // Assert the snack bar is shown and tap "Save".
2444                 sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
2445 
2446                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
2447 
2448                 // Assert value of expected fields - should not be sanitized.
2449                 final ViewNode usernameNode = findNodeByResourceId(saveRequest.structure,
2450                         ID_USERNAME);
2451                 assertTextAndValue(usernameNode, username);
2452                 final ViewNode passwordNode = findNodeByResourceId(saveRequest.structure,
2453                         ID_PASSWORD);
2454                 assertTextAndValue(passwordNode, password);
2455 
2456                 waitUntilDisconnected();
2457                 assertNoDanglingSessions();
2458 
2459             } catch (RetryableException e) {
2460                 throw e;
2461             } catch (Throwable t) {
2462                 throw new Throwable("Error on step " + i, t);
2463             }
2464         }
2465     }
2466 
2467     @Test
testCancelMultipleTimes()2468     public void testCancelMultipleTimes() throws Throwable {
2469         // Set service.
2470         enableService();
2471 
2472         for (int i = 1; i <= 3; i++) {
2473             final String username = "user-" + i;
2474             final String password = "pass-" + i;
2475             sReplier.addResponse(new CannedDataset.Builder()
2476                     .setField(ID_USERNAME, username)
2477                     .setField(ID_PASSWORD, password)
2478                     .setPresentation(createPresentation("The Dude"))
2479                     .build());
2480             mActivity.expectAutoFill(username, password);
2481             try {
2482                 // Trigger auto-fill.
2483                 mActivity.onUsername(View::requestFocus);
2484 
2485                 waitUntilConnected();
2486                 sReplier.getNextFillRequest();
2487 
2488                 // Auto-fill it.
2489                 sUiBot.selectDataset("The Dude");
2490 
2491                 // Check the results.
2492                 mActivity.assertAutoFilled();
2493 
2494                 // Change focus to prepare for next step - must do it before session is gone
2495                 mActivity.onPassword(View::requestFocus);
2496 
2497                 // Rinse and repeat...
2498                 mActivity.tapClear();
2499 
2500                 waitUntilDisconnected();
2501                 assertNoDanglingSessions();
2502             } catch (RetryableException e) {
2503                 throw e;
2504             } catch (Throwable t) {
2505                 throw new Throwable("Error on step " + i, t);
2506             }
2507         }
2508     }
2509 
2510     @Test
testClickCustomButton()2511     public void testClickCustomButton() throws Exception {
2512         // Set service.
2513         enableService();
2514 
2515         Intent intent = new Intent(getContext(), EmptyActivity.class);
2516         IntentSender sender = PendingIntent.getActivity(getContext(), 0, intent,
2517                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT)
2518                 .getIntentSender();
2519 
2520         RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
2521                 R.layout.list_item);
2522         presentation.setTextViewText(R.id.text1, "Poke");
2523         Intent firstIntent = new Intent(getContext(), DummyActivity.class);
2524         presentation.setOnClickPendingIntent(R.id.text1, PendingIntent.getActivity(
2525                 getContext(), 0, firstIntent, PendingIntent.FLAG_ONE_SHOT
2526                         | PendingIntent.FLAG_CANCEL_CURRENT));
2527 
2528         // Set expectations.
2529         sReplier.addResponse(new CannedFillResponse.Builder()
2530                 .setAuthentication(sender, ID_USERNAME)
2531                 .setPresentation(presentation)
2532                 .build());
2533 
2534         // Trigger auto-fill.
2535         mActivity.onUsername(View::requestFocus);
2536 
2537         // Wait for onFill() before proceeding.
2538         sReplier.getNextFillRequest();
2539 
2540         // Click on the custom button
2541         sUiBot.selectByText("Poke");
2542 
2543         // Make sure the click worked
2544         sUiBot.selectByText("foo");
2545 
2546         // Go back to the filled app.
2547         sUiBot.pressBack();
2548 
2549         // The session should be gone
2550         assertNoDanglingSessions();
2551     }
2552 
2553     @Test
checkFillSelectionAfterSelectingDatasetAuthentication()2554     public void checkFillSelectionAfterSelectingDatasetAuthentication() throws Exception {
2555         enableService();
2556 
2557         // Set up FillResponse with dataset authentication
2558         Bundle clientState = new Bundle();
2559         clientState.putCharSequence("clientStateKey", "clientStateValue");
2560 
2561         // Prepare the authenticated response
2562         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
2563                 new CannedDataset.Builder()
2564                         .setField(ID_USERNAME, "dude")
2565                         .setField(ID_PASSWORD, "sweet")
2566                         .setPresentation(createPresentation("Dataset"))
2567                         .build());
2568 
2569         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
2570                 new CannedDataset.Builder()
2571                         .setField(ID_USERNAME, "username")
2572                         .setId("name")
2573                         .setPresentation(createPresentation("authentication"))
2574                         .setAuthentication(authentication)
2575                         .build())
2576                 .setExtras(clientState).build());
2577 
2578         // Trigger autofill.
2579         mActivity.onUsername(View::requestFocus);
2580 
2581         // Authenticate
2582         sUiBot.selectDataset("authentication");
2583         sReplier.getNextFillRequest();
2584 
2585         eventually(() -> {
2586             // Verify fill selection
2587             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2588                     .getFillEventHistory();
2589             assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
2590                     "clientStateValue");
2591 
2592             assertThat(selection.getEvents().size()).isEqualTo(1);
2593             FillEventHistory.Event event = selection.getEvents().get(0);
2594             assertThat(event.getType()).isEqualTo(TYPE_DATASET_AUTHENTICATION_SELECTED);
2595             assertThat(event.getDatasetId()).isEqualTo("name");
2596         });
2597     }
2598 
2599     @Test
checkFillSelectionAfterSelectingAuthentication()2600     public void checkFillSelectionAfterSelectingAuthentication() throws Exception {
2601         enableService();
2602 
2603         // Set up FillResponse with response wide authentication
2604         Bundle clientState = new Bundle();
2605         clientState.putCharSequence("clientStateKey", "clientStateValue");
2606 
2607         // Prepare the authenticated response
2608         final IntentSender authentication = AuthenticationActivity.createSender(getContext(), 1,
2609                 new CannedFillResponse.Builder().addDataset(
2610                         new CannedDataset.Builder()
2611                                 .setField(ID_USERNAME, "username")
2612                                 .setId("name")
2613                                 .setPresentation(createPresentation("dataset"))
2614                                 .build())
2615                         .setExtras(clientState).build());
2616 
2617         sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
2618                 .setPresentation(createPresentation("authentication"))
2619                 .setAuthentication(authentication, ID_USERNAME)
2620                 .build());
2621 
2622         // Trigger autofill.
2623         mActivity.onUsername(View::requestFocus);
2624 
2625         // Authenticate
2626         sUiBot.selectDataset("authentication");
2627         sReplier.getNextFillRequest();
2628 
2629         eventually(() -> {
2630             // Verify fill selection
2631             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2632                     .getFillEventHistory();
2633             assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
2634                     "clientStateValue");
2635 
2636             assertThat(selection.getEvents().size()).isEqualTo(1);
2637             FillEventHistory.Event event = selection.getEvents().get(0);
2638             assertThat(event.getType()).isEqualTo(TYPE_AUTHENTICATION_SELECTED);
2639             assertThat(event.getDatasetId()).isNull();
2640         });
2641     }
2642 
2643     @Test
checkFillSelectionAfterSelectingTwoDatasets()2644     public void checkFillSelectionAfterSelectingTwoDatasets() throws Exception {
2645         enableService();
2646 
2647         // Set up first partition with an anonymous dataset
2648         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
2649                 new CannedDataset.Builder()
2650                         .setField(ID_USERNAME, "username")
2651                         .setPresentation(createPresentation("dataset1"))
2652                         .build())
2653                 .build());
2654 
2655         // Trigger autofill on username
2656         mActivity.onUsername(View::requestFocus);
2657         sUiBot.selectDataset("dataset1");
2658         sReplier.getNextFillRequest();
2659 
2660         eventually(() -> {
2661             // Verify fill selection
2662             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2663                     .getFillEventHistory();
2664             assertThat(selection.getClientState()).isNull();
2665 
2666             assertThat(selection.getEvents().size()).isEqualTo(1);
2667             FillEventHistory.Event event = selection.getEvents().get(0);
2668             assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2669             assertThat(event.getDatasetId()).isNull();
2670         });
2671 
2672         // Set up second partition with a named dataset
2673         Bundle clientState = new Bundle();
2674         clientState.putCharSequence("clientStateKey", "clientStateValue");
2675 
2676         sReplier.addResponse(new CannedFillResponse.Builder()
2677                 .addDataset(
2678                         new CannedDataset.Builder()
2679                                 .setField(ID_PASSWORD, "password2")
2680                                 .setPresentation(createPresentation("dataset2"))
2681                                 .setId("name2")
2682                                 .build())
2683                 .addDataset(
2684                         new CannedDataset.Builder()
2685                                 .setField(ID_PASSWORD, "password3")
2686                                 .setPresentation(createPresentation("dataset3"))
2687                                 .setId("name3")
2688                                 .build())
2689                 .setExtras(clientState)
2690                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build());
2691 
2692         // Trigger autofill on password
2693         mActivity.onPassword(View::requestFocus);
2694         sUiBot.selectDataset("dataset3");
2695         sReplier.getNextFillRequest();
2696 
2697         eventually(() -> {
2698             // Verify fill selection
2699             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2700                     .getFillEventHistory();
2701             assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
2702                     "clientStateValue");
2703 
2704             assertThat(selection.getEvents().size()).isEqualTo(1);
2705             FillEventHistory.Event event = selection.getEvents().get(0);
2706             assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2707             assertThat(event.getDatasetId()).isEqualTo("name3");
2708         });
2709 
2710         mActivity.onPassword((v) -> v.setText("new password"));
2711         mActivity.syncRunOnUiThread(() -> mActivity.finish());
2712 
2713         eventually(() -> {
2714             // Verify fill selection
2715             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2716                     .getFillEventHistory();
2717             assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
2718                     "clientStateValue");
2719 
2720             assertThat(selection.getEvents().size()).isEqualTo(2);
2721             FillEventHistory.Event event1 = selection.getEvents().get(0);
2722             assertThat(event1.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2723             assertThat(event1.getDatasetId()).isEqualTo("name3");
2724 
2725             FillEventHistory.Event event2 = selection.getEvents().get(1);
2726             assertThat(event2.getType()).isEqualTo(TYPE_SAVE_SHOWN);
2727             assertThat(event2.getDatasetId()).isNull();
2728         });
2729     }
2730 
2731     @Test
checkFillSelectionIsResetAfterReturningNull()2732     public void checkFillSelectionIsResetAfterReturningNull() throws Exception {
2733         enableService();
2734 
2735         // First reset
2736         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
2737                 new CannedDataset.Builder()
2738                         .setField(ID_USERNAME, "username")
2739                         .setPresentation(createPresentation("dataset1"))
2740                         .build())
2741                 .build());
2742         mActivity.onUsername(View::requestFocus);
2743         sReplier.getNextFillRequest();
2744         sUiBot.selectDataset("dataset1");
2745 
2746         eventually(() -> {
2747             // Verify fill selection
2748             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2749                     .getFillEventHistory();
2750             assertThat(selection.getClientState()).isNull();
2751 
2752             assertThat(selection.getEvents().size()).isEqualTo(1);
2753             FillEventHistory.Event event = selection.getEvents().get(0);
2754             assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2755             assertThat(event.getDatasetId()).isNull();
2756         });
2757 
2758         // Second request
2759         sReplier.addResponse(NO_RESPONSE);
2760         mActivity.onPassword(View::requestFocus);
2761         sReplier.getNextFillRequest();
2762         sUiBot.assertNoDatasets();
2763 
2764         eventually(() -> {
2765             // Verify fill selection
2766             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2767                     .getFillEventHistory();
2768             assertThat(selection).isNull();
2769         });
2770     }
2771 
2772     @Test
checkFillSelectionIsResetAfterReturningError()2773     public void checkFillSelectionIsResetAfterReturningError() throws Exception {
2774         enableService();
2775 
2776         // First reset
2777         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
2778                 new CannedDataset.Builder()
2779                         .setField(ID_USERNAME, "username")
2780                         .setPresentation(createPresentation("dataset1"))
2781                         .build())
2782                 .build());
2783         mActivity.onUsername(View::requestFocus);
2784         waitUntilConnected();
2785         sReplier.getNextFillRequest();
2786         sUiBot.selectDataset("dataset1");
2787 
2788         eventually(() -> {
2789             // Verify fill selection
2790             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2791                     .getFillEventHistory();
2792             assertThat(selection.getClientState()).isNull();
2793 
2794             assertThat(selection.getEvents().size()).isEqualTo(1);
2795             FillEventHistory.Event event = selection.getEvents().get(0);
2796             assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2797             assertThat(event.getDatasetId()).isNull();
2798         });
2799 
2800         // Second request
2801         sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build());
2802         mActivity.onPassword(View::requestFocus);
2803         sReplier.getNextFillRequest();
2804         sUiBot.assertNoDatasets();
2805 
2806         eventually(() -> {
2807             // Verify fill selection
2808             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2809                     .getFillEventHistory();
2810             assertThat(selection).isNull();
2811         });
2812     }
2813 
2814     @Test
checkFillSelectionIsResetAfterTimeout()2815     public void checkFillSelectionIsResetAfterTimeout() throws Exception {
2816         enableService();
2817 
2818         // First reset
2819         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
2820                 new CannedDataset.Builder()
2821                         .setField(ID_USERNAME, "username")
2822                         .setPresentation(createPresentation("dataset1"))
2823                         .build())
2824                 .build());
2825         mActivity.onUsername(View::requestFocus);
2826         waitUntilConnected();
2827         sReplier.getNextFillRequest();
2828         sUiBot.selectDataset("dataset1");
2829 
2830         eventually(() -> {
2831             // Verify fill selection
2832             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2833                     .getFillEventHistory();
2834             assertThat(selection.getClientState()).isNull();
2835 
2836             assertThat(selection.getEvents().size()).isEqualTo(1);
2837             FillEventHistory.Event event = selection.getEvents().get(0);
2838             assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
2839             assertThat(event.getDatasetId()).isNull();
2840         });
2841 
2842         // Second request
2843         sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
2844         mActivity.onPassword(View::requestFocus);
2845         sReplier.getNextFillRequest();
2846         waitUntilDisconnected();
2847 
2848         eventually(() -> {
2849             // Verify fill selection
2850             FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
2851                     .getFillEventHistory();
2852             assertThat(selection).isNull();
2853         });
2854     }
2855 
getBundle(String key, String value)2856     private Bundle getBundle(String key, String value) {
2857         final Bundle bundle = new Bundle();
2858         bundle.putString(key, value);
2859         return bundle;
2860     }
2861 
2862     /**
2863      * Tests the following scenario:
2864      *
2865      * <ol>
2866      *    <li>Activity A is launched.
2867      *    <li>Activity A triggers autofill.
2868      *    <li>Activity B is launched.
2869      *    <li>Activity B triggers autofill.
2870      *    <li>User goes back to Activity A.
2871      *    <li>User triggers save on Activity A - at this point, service should have stats of
2872      *        activity B, and stats for activity A should have beeen discarded.
2873      * </ol>
2874      */
2875     @Test
checkFillSelectionFromPreviousSessionIsDiscarded()2876     public void checkFillSelectionFromPreviousSessionIsDiscarded() throws Exception {
2877         enableService();
2878 
2879         // Launch activity A
2880         sReplier.addResponse(new CannedFillResponse.Builder()
2881                 .setExtras(getBundle("activity", "A"))
2882                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2883                 .build());
2884 
2885         // Trigger autofill on activity A
2886         mActivity.onUsername(View::requestFocus);
2887         waitUntilConnected();
2888         sReplier.getNextFillRequest();
2889 
2890         // Verify fill selection for Activity A
2891         FillEventHistory selectionA = InstrumentedAutoFillService.peekInstance()
2892                 .getFillEventHistory();
2893         assertThat(selectionA.getClientState().getString("activity")).isEqualTo("A");
2894         assertThat(selectionA.getEvents()).isNull();
2895 
2896         // Launch activity B
2897         getContext().startActivity(new Intent(getContext(), CheckoutActivity.class));
2898 
2899         // Trigger autofill on activity B
2900         sReplier.addResponse(new CannedFillResponse.Builder()
2901                 .setExtras(getBundle("activity", "B"))
2902                 .addDataset(new CannedDataset.Builder()
2903                         .setField(ID_CC_NUMBER, "4815162342")
2904                         .setPresentation(createPresentation("datasetB"))
2905                         .build())
2906                 .build());
2907         sUiBot.focusByRelativeId(ID_CC_NUMBER);
2908         sReplier.getNextFillRequest();
2909 
2910         // Verify fill selection for Activity B
2911         final FillEventHistory selectionB = InstrumentedAutoFillService.peekInstance()
2912                 .getFillEventHistory();
2913         assertThat(selectionB.getClientState().getString("activity")).isEqualTo("B");
2914         assertThat(selectionB.getEvents()).isNull();
2915 
2916         // Now switch back to A...
2917         sUiBot.pressBack(); // dismiss keyboard
2918         sUiBot.pressBack(); // dismiss task
2919         // ...and trigger save
2920         // Set credentials...
2921         mActivity.onUsername((v) -> v.setText("malkovich"));
2922         mActivity.onPassword((v) -> v.setText("malkovich"));
2923         final String expectedMessage = getWelcomeMessage("malkovich");
2924         final String actualMessage = mActivity.tapLogin();
2925         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
2926         sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
2927         sReplier.getNextSaveRequest();
2928 
2929         // Finally, make sure history is right
2930         final FillEventHistory finalSelection = InstrumentedAutoFillService.peekInstance()
2931                 .getFillEventHistory();
2932         assertThat(finalSelection.getClientState().getString("activity")).isEqualTo("B");
2933         assertThat(finalSelection.getEvents()).isNull();
2934 
2935     }
2936 
2937     @Test
testIsServiceEnabled()2938     public void testIsServiceEnabled() throws Exception {
2939         disableService();
2940         final AutofillManager afm = mActivity.getAutofillManager();
2941         assertThat(afm.hasEnabledAutofillServices()).isFalse();
2942         try {
2943             enableService();
2944             assertThat(afm.hasEnabledAutofillServices()).isTrue();
2945         } finally {
2946             disableService();
2947         }
2948     }
2949 
2950     @Test
testSetupComplete()2951     public void testSetupComplete() throws Exception {
2952         enableService();
2953 
2954         // Sanity check.
2955         final AutofillManager afm = mActivity.getAutofillManager();
2956         assertThat(afm.isEnabled()).isTrue();
2957 
2958         // Now disable user_complete and try again.
2959         try {
2960             setUserComplete(getContext(), false);
2961             assertThat(afm.isEnabled()).isFalse();
2962         } finally {
2963             setUserComplete(getContext(), true);
2964         }
2965     }
2966 }
2967