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