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