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