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                     windowManager.removeView(overlay[0]);
1264                 });
1265             } catch (Exception e) {
1266                 mSafeCleanerRule.add(e);
1267             }
1268         }
1269     }
1270 
1271     @Test
1272     @AppModeFull(reason = "testAutoFillOneDataset() is enough")
testAutoFillMultipleDatasetsPickFirst()1273     public void testAutoFillMultipleDatasetsPickFirst() throws Exception {
1274         multipleDatasetsTest(1);
1275     }
1276 
1277     @Test
1278     @AppModeFull(reason = "testAutoFillOneDataset() is enough")
testAutoFillMultipleDatasetsPickSecond()1279     public void testAutoFillMultipleDatasetsPickSecond() throws Exception {
1280         multipleDatasetsTest(2);
1281     }
1282 
1283     @Test
1284     @AppModeFull(reason = "testAutoFillOneDataset() is enough")
testAutoFillMultipleDatasetsPickThird()1285     public void testAutoFillMultipleDatasetsPickThird() throws Exception {
1286         multipleDatasetsTest(3);
1287     }
1288 
multipleDatasetsTest(int number)1289     private void multipleDatasetsTest(int number) throws Exception {
1290         // Set service.
1291         enableService();
1292 
1293         // Set expectations.
1294         sReplier.addResponse(new CannedFillResponse.Builder()
1295                 .addDataset(new CannedDataset.Builder()
1296                         .setField(ID_USERNAME, "mr_plow")
1297                         .setField(ID_PASSWORD, "D'OH!")
1298                         .setPresentation(createPresentation("Mr Plow"))
1299                         .build())
1300                 .addDataset(new CannedDataset.Builder()
1301                         .setField(ID_USERNAME, "el barto")
1302                         .setField(ID_PASSWORD, "aycaramba!")
1303                         .setPresentation(createPresentation("El Barto"))
1304                         .build())
1305                 .addDataset(new CannedDataset.Builder()
1306                         .setField(ID_USERNAME, "mr sparkle")
1307                         .setField(ID_PASSWORD, "Aw3someP0wer")
1308                         .setPresentation(createPresentation("Mr Sparkle"))
1309                         .build())
1310                 .build());
1311         final String name;
1312 
1313         switch (number) {
1314             case 1:
1315                 name = "Mr Plow";
1316                 mActivity.expectAutoFill("mr_plow", "D'OH!");
1317                 break;
1318             case 2:
1319                 name = "El Barto";
1320                 mActivity.expectAutoFill("el barto", "aycaramba!");
1321                 break;
1322             case 3:
1323                 name = "Mr Sparkle";
1324                 mActivity.expectAutoFill("mr sparkle", "Aw3someP0wer");
1325                 break;
1326             default:
1327                 throw new IllegalArgumentException("invalid dataset number: " + number);
1328         }
1329 
1330         // Trigger auto-fill.
1331         requestFocusOnUsername();
1332         sReplier.getNextFillRequest();
1333 
1334         // Make sure all datasets are shown.
1335         final UiObject2 picker = mUiBot.assertDatasets("Mr Plow", "El Barto", "Mr Sparkle");
1336 
1337         // Auto-fill it.
1338         mUiBot.selectDataset(picker, name);
1339 
1340         // Check the results.
1341         mActivity.assertAutoFilled();
1342     }
1343 
1344     /**
1345      * Tests the scenario where the service uses custom remote views for different fields (username
1346      * and password).
1347      */
1348     @Presubmit
1349     @Test
testAutofillOneDatasetCustomPresentation()1350     public void testAutofillOneDatasetCustomPresentation() throws Exception {
1351         // Set service.
1352         enableService();
1353 
1354         // Set expectations.
1355         sReplier.addResponse(new CannedDataset.Builder()
1356                 .setField(ID_USERNAME, "dude",
1357                         createPresentation("The Dude"))
1358                 .setField(ID_PASSWORD, "sweet",
1359                         createPresentation("Dude's password"))
1360                 .build());
1361         mActivity.expectAutoFill("dude", "sweet");
1362 
1363         // Trigger auto-fill.
1364         requestFocusOnUsername();
1365         sReplier.getNextFillRequest();
1366 
1367         // Check initial field.
1368         mUiBot.assertDatasets("The Dude");
1369 
1370         // Then move around...
1371         requestFocusOnPassword();
1372         mUiBot.assertDatasets("Dude's password");
1373         requestFocusOnUsername();
1374         mUiBot.assertDatasets("The Dude");
1375 
1376         // Auto-fill it.
1377         requestFocusOnPassword();
1378         mUiBot.selectDataset("Dude's password");
1379 
1380         // Check the results.
1381         mActivity.assertAutoFilled();
1382     }
1383 
1384     /**
1385      * Tests the scenario where the service uses custom remote views for different fields (username
1386      * and password) and the dataset itself, and each dataset has the same number of fields.
1387      */
1388     @Test
1389     @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough")
testAutofillMultipleDatasetsCustomPresentations()1390     public void testAutofillMultipleDatasetsCustomPresentations() throws Exception {
1391         // Set service.
1392         enableService();
1393 
1394         // Set expectations.
1395         sReplier.addResponse(new CannedFillResponse.Builder()
1396                 .addDataset(new CannedDataset.Builder(createPresentation("Dataset1"))
1397                         .setField(ID_USERNAME, "user1") // no presentation
1398                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
1399                         .build())
1400                 .addDataset(new CannedDataset.Builder()
1401                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
1402                         .setField(ID_PASSWORD, "pass2") // no presentation
1403                         .setPresentation(createPresentation("Dataset2"))
1404                         .build())
1405                 .build());
1406         mActivity.expectAutoFill("user1", "pass1");
1407 
1408         // Trigger auto-fill.
1409         requestFocusOnUsername();
1410         sReplier.getNextFillRequest();
1411 
1412         // Check initial field.
1413         mUiBot.assertDatasets("Dataset1", "User2");
1414 
1415         // Then move around...
1416         requestFocusOnPassword();
1417         mUiBot.assertDatasets("Pass1", "Dataset2");
1418         requestFocusOnUsername();
1419         mUiBot.assertDatasets("Dataset1", "User2");
1420 
1421         // Auto-fill it.
1422         requestFocusOnPassword();
1423         mUiBot.selectDataset("Pass1");
1424 
1425         // Check the results.
1426         mActivity.assertAutoFilled();
1427     }
1428 
1429     /**
1430      * Tests the scenario where the service uses custom remote views for different fields (username
1431      * and password), and each dataset has the same number of fields.
1432      */
1433     @Test
1434     @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough")
testAutofillMultipleDatasetsCustomPresentationSameFields()1435     public void testAutofillMultipleDatasetsCustomPresentationSameFields() throws Exception {
1436         // Set service.
1437         enableService();
1438 
1439         // Set expectations.
1440         sReplier.addResponse(new CannedFillResponse.Builder()
1441                 .addDataset(new CannedDataset.Builder()
1442                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
1443                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
1444                         .build())
1445                 .addDataset(new CannedDataset.Builder()
1446                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
1447                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
1448                         .build())
1449                 .build());
1450         mActivity.expectAutoFill("user1", "pass1");
1451 
1452         // Trigger auto-fill.
1453         requestFocusOnUsername();
1454         sReplier.getNextFillRequest();
1455 
1456         // Check initial field.
1457         mUiBot.assertDatasets("User1", "User2");
1458 
1459         // Then move around...
1460         requestFocusOnPassword();
1461         mUiBot.assertDatasets("Pass1", "Pass2");
1462         requestFocusOnUsername();
1463         mUiBot.assertDatasets("User1", "User2");
1464 
1465         // Auto-fill it.
1466         requestFocusOnPassword();
1467         mUiBot.selectDataset("Pass1");
1468 
1469         // Check the results.
1470         mActivity.assertAutoFilled();
1471     }
1472 
1473     /**
1474      * Tests the scenario where the service uses custom remote views for different fields (username
1475      * and password), but each dataset has a different number of fields.
1476      */
1477     @Test
1478     @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough")
testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()1479     public void testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()
1480             throws Exception {
1481         // Set service.
1482         enableService();
1483 
1484         // Set expectations.
1485         sReplier.addResponse(new CannedFillResponse.Builder()
1486                 .addDataset(new CannedDataset.Builder()
1487                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
1488                         .build())
1489                 .addDataset(new CannedDataset.Builder()
1490                         .setField(ID_USERNAME, "user2", createPresentation("User2"))
1491                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
1492                         .build())
1493                 .build());
1494         mActivity.expectAutoFill("user2", "pass2");
1495 
1496         // Trigger auto-fill.
1497         requestFocusOnUsername();
1498         sReplier.getNextFillRequest();
1499 
1500         // Check initial field.
1501         mUiBot.assertDatasets("User1", "User2");
1502 
1503         // Then move around...
1504         requestFocusOnPassword();
1505         mUiBot.assertDatasets("Pass2");
1506         requestFocusOnUsername();
1507         mUiBot.assertDatasets("User1", "User2");
1508 
1509         // Auto-fill it.
1510         mUiBot.selectDataset("User2");
1511 
1512         // Check the results.
1513         mActivity.assertAutoFilled();
1514     }
1515 
1516     @Test
1517     @AsbSecurityTest(cveBugId = 281533566)
remoteViews_doesNotSpillAcrossUsers()1518     public void remoteViews_doesNotSpillAcrossUsers() throws Exception {
1519         // Set service.
1520         enableService();
1521 
1522 
1523         RemoteViews firstRv = createPresentation("hello");
1524         RemoteViews secondRv = createPresentation("world");
1525 
1526         // bad url, should not be displayed
1527         firstRv.setImageViewIcon(R.id.icon,
1528                 Icon.createWithContentUri("content://1000@com.android.contacts/display_photo/1"));
1529         secondRv.setImageViewIcon(R.id.icon,
1530                 Icon.createWithContentUri("content://1000@com.android.contacts/display_photo/1"));
1531 
1532         // Set expectations.
1533         sReplier.addResponse(new CannedFillResponse.Builder()
1534                 .addDataset(new CannedDataset.Builder()
1535                     .setField(ID_USERNAME, "dude", firstRv)
1536                     .setField(ID_PASSWORD, "sweet", secondRv)
1537                     .build())
1538                 .setHeader(firstRv)
1539                 .setFooter(secondRv)
1540                 .build());
1541 
1542         mActivity.expectAutoFill("dude", "sweet");
1543 
1544         // Trigger auto-fill.
1545         mActivity.onUsername(View::requestFocus);
1546         sReplier.getNextFillRequest();
1547 
1548         // Assert that the dataset is not shown
1549         assertThrows(RetryableException.class,
1550                 () -> mUiBot.assertDatasets("The Dude"));
1551 
1552         // Assert that header/footer is not shown
1553         assertThrows(RetryableException.class,
1554                 () -> mUiBot.assertDatasetsWithBorders("hello", "world", "The Dude"));
1555     }
1556 
1557     /**
1558      * Tests the scenario where the service uses custom remote views for different fields (username
1559      * and password), but each dataset has a different number of fields.
1560      */
1561     @Test
1562     @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough")
testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()1563     public void testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()
1564             throws Exception {
1565         // Set service.
1566         enableService();
1567 
1568         // Set expectations.
1569         sReplier.addResponse(new CannedFillResponse.Builder()
1570                 .addDataset(new CannedDataset.Builder()
1571                         .setField(ID_USERNAME, "user1", createPresentation("User1"))
1572                         .setField(ID_PASSWORD, "pass1", createPresentation("Pass1"))
1573                         .build())
1574                 .addDataset(new CannedDataset.Builder()
1575                         .setField(ID_PASSWORD, "pass2", createPresentation("Pass2"))
1576                         .build())
1577                 .build());
1578         mActivity.expectAutoFill("user1", "pass1");
1579 
1580         // Trigger auto-fill.
1581         requestFocusOnUsername();
1582         sReplier.getNextFillRequest();
1583 
1584         // Check initial field.
1585         mUiBot.assertDatasets("User1");
1586 
1587         // Then move around...
1588         requestFocusOnPassword();
1589         mUiBot.assertDatasets("Pass1", "Pass2");
1590         requestFocusOnUsername();
1591         mUiBot.assertDatasets("User1");
1592 
1593         // Auto-fill it.
1594         mUiBot.selectDataset("User1");
1595 
1596         // Check the results.
1597         mActivity.assertAutoFilled();
1598     }
1599 
1600     @Test
1601     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnly()1602     public void testSaveOnly() throws Exception {
1603         saveOnlyTest(false);
1604     }
1605 
1606     @Test
1607     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnlyTriggeredManually()1608     public void testSaveOnlyTriggeredManually() throws Exception {
1609         saveOnlyTest(false);
1610     }
1611 
saveOnlyTest(boolean manually)1612     private void saveOnlyTest(boolean manually) throws Exception {
1613         enableService();
1614 
1615         // Set expectations.
1616         sReplier.addResponse(new CannedFillResponse.Builder()
1617                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1618                 .build());
1619 
1620         // Trigger auto-fill.
1621         if (manually) {
1622             mActivity.forceAutofillOnUsername();
1623         } else {
1624             mActivity.onUsername(View::requestFocus);
1625         }
1626 
1627         // Validation check.
1628         mUiBot.assertNoDatasetsEver();
1629 
1630         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1631         // the session started
1632         sReplier.getNextFillRequest();
1633 
1634         // Set credentials...
1635         mActivity.onUsername((v) -> v.setText("malkovich"));
1636         mActivity.onPassword((v) -> v.setText("malkovich"));
1637 
1638         // ...and login
1639         final String expectedMessage = getWelcomeMessage("malkovich");
1640         final String actualMessage = mActivity.tapLogin();
1641         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1642 
1643         // Assert the snack bar is shown and tap "Save".
1644         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1645 
1646         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1647         sReplier.assertNoUnhandledSaveRequests();
1648         assertThat(saveRequest.datasetIds).isNull();
1649 
1650         // Assert value of expected fields - should not be sanitized.
1651         try {
1652             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1653             assertTextAndValue(username, "malkovich");
1654             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1655             assertTextAndValue(password, "malkovich");
1656         } catch (AssertionError | RuntimeException e) {
1657             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1658             throw e;
1659         }
1660     }
1661 
1662     @Test
testSaveGoesAwayWhenTappingHomeButton()1663     public void testSaveGoesAwayWhenTappingHomeButton() throws Exception {
1664         saveGoesAway(DismissType.HOME_BUTTON);
1665     }
1666 
1667     @Test
testSaveGoesAwayWhenTappingBackButton()1668     public void testSaveGoesAwayWhenTappingBackButton() throws Exception {
1669         saveGoesAway(DismissType.BACK_BUTTON);
1670     }
1671 
1672     @Test
testSaveGoesAwayWhenTouchingOutside()1673     public void testSaveGoesAwayWhenTouchingOutside() throws Exception {
1674         saveGoesAway(DismissType.TOUCH_OUTSIDE);
1675     }
1676 
saveGoesAway(DismissType dismissType)1677     private void saveGoesAway(DismissType dismissType) throws Exception {
1678         enableService();
1679 
1680         // Set expectations.
1681         sReplier.addResponse(new CannedFillResponse.Builder()
1682                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1683                 .build());
1684 
1685         // Trigger auto-fill.
1686         mActivity.onUsername(View::requestFocus);
1687 
1688         // Validation check.
1689         mUiBot.assertNoDatasetsEver();
1690 
1691         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1692         // the session started
1693         sReplier.getNextFillRequest();
1694 
1695         // Set credentials...
1696         mActivity.onUsername((v) -> v.setText("malkovich"));
1697         mActivity.onPassword((v) -> v.setText("malkovich"));
1698 
1699         // ...and login
1700         final String expectedMessage = getWelcomeMessage("malkovich");
1701         final String actualMessage = mActivity.tapLogin();
1702         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1703 
1704         // Assert the snack bar is shown and tap "Save".
1705         mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
1706 
1707         // Then make sure it goes away when user doesn't want it..
1708         String when;
1709         switch (dismissType) {
1710             case BACK_BUTTON:
1711                 when = "back button tapped";
1712                 mUiBot.pressBack();
1713                 break;
1714             case HOME_BUTTON:
1715                 when = "home button tapped";
1716                 mUiBot.pressHome();
1717                 break;
1718             case TOUCH_OUTSIDE:
1719                 when = "touched outside";
1720                 mUiBot.touchOutsideSaveDialog();
1721                 break;
1722             default:
1723                 throw new IllegalArgumentException("invalid dismiss type: " + dismissType);
1724         }
1725         mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD, when);
1726     }
1727 
1728     @Test
1729     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnlyPreFilled()1730     public void testSaveOnlyPreFilled() throws Exception {
1731         saveOnlyTestPreFilled(false);
1732     }
1733 
1734     @Test
1735     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnlyTriggeredManuallyPreFilled()1736     public void testSaveOnlyTriggeredManuallyPreFilled() throws Exception {
1737         saveOnlyTestPreFilled(true);
1738     }
1739 
saveOnlyTestPreFilled(boolean manually)1740     private void saveOnlyTestPreFilled(boolean manually) throws Exception {
1741         enableService();
1742 
1743         // Set expectations.
1744         sReplier.addResponse(new CannedFillResponse.Builder()
1745                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1746                 .build());
1747 
1748         // Set activity
1749         mActivity.onUsername((v) -> v.setText("user_before"));
1750         mActivity.onPassword((v) -> v.setText("pass_before"));
1751 
1752         // Trigger auto-fill.
1753         if (manually) {
1754             // setText() will trigger a fill request.
1755             // Waits the first fill request triggered by the setText() is received by the service to
1756             // avoid flaky.
1757             sReplier.getNextFillRequest();
1758             mUiBot.waitForIdle();
1759 
1760             // Set expectations again.
1761             sReplier.addResponse(new CannedFillResponse.Builder()
1762                     .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1763                     .build());
1764             mActivity.forceAutofillOnUsername();
1765         } else {
1766             mUiBot.selectByRelativeId(ID_USERNAME);
1767         }
1768         mUiBot.waitForIdle();
1769 
1770         // Validation check.
1771         mUiBot.assertNoDatasetsEver();
1772 
1773         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1774         // the session started
1775         sReplier.getNextFillRequest();
1776 
1777         // Set credentials...
1778         mActivity.onUsername((v) -> v.setText("user_after"));
1779         mActivity.onPassword((v) -> v.setText("pass_after"));
1780 
1781         // ...and login
1782         final String expectedMessage = getWelcomeMessage("user_after");
1783         final String actualMessage = mActivity.tapLogin();
1784         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1785         mUiBot.waitForIdle();
1786 
1787         // Assert the snack bar is shown and tap "Save".
1788         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1789 
1790         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1791         sReplier.assertNoUnhandledSaveRequests();
1792 
1793         // Assert value of expected fields - should not be sanitized.
1794         try {
1795             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1796             assertTextAndValue(username, "user_after");
1797             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1798             assertTextAndValue(password, "pass_after");
1799         } catch (AssertionError | RuntimeException e) {
1800             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1801             throw e;
1802         }
1803     }
1804 
1805     @Test
1806     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnlyTwoRequiredFieldsOnePrefilled()1807     public void testSaveOnlyTwoRequiredFieldsOnePrefilled() throws Exception {
1808         enableService();
1809 
1810         // Set expectations.
1811         sReplier.addResponse(new CannedFillResponse.Builder()
1812                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
1813                 .build());
1814 
1815         // Set activity
1816         mActivity.onUsername((v) -> v.setText("I_AM_USER"));
1817 
1818         // Trigger auto-fill.
1819         mActivity.onPassword(View::requestFocus);
1820 
1821         // Wait for onFill() before changing value, otherwise the fields might be changed before
1822         // the session started
1823         sReplier.getNextFillRequest();
1824         mUiBot.assertNoDatasetsEver();
1825 
1826         // Set credentials...
1827         mActivity.onPassword((v) -> v.setText("thou should pass")); // contains pass
1828 
1829         // ...and login
1830         final String expectedMessage = getWelcomeMessage("I_AM_USER"); // contains pass
1831         final String actualMessage = mActivity.tapLogin();
1832         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1833 
1834         // Assert the snack bar is shown and tap "Save".
1835         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1836 
1837         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1838         sReplier.assertNoUnhandledSaveRequests();
1839 
1840         // Assert value of expected fields - should not be sanitized.
1841         try {
1842             final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1843             assertTextAndValue(username, "I_AM_USER");
1844             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1845             assertTextAndValue(password, "thou should pass");
1846         } catch (AssertionError | RuntimeException e) {
1847             dumpStructure("saveOnlyTest() failed", saveRequest.structure);
1848             throw e;
1849         }
1850     }
1851 
1852     @Test
1853     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveOnlyOptionalField()1854     public void testSaveOnlyOptionalField() throws Exception {
1855         enableService();
1856 
1857         // Set expectations.
1858         sReplier.addResponse(new CannedFillResponse.Builder()
1859                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME)
1860                 .setOptionalSavableIds(ID_PASSWORD)
1861                 .build());
1862 
1863         // Trigger auto-fill.
1864         mActivity.onUsername(View::requestFocus);
1865 
1866         // Validation check.
1867         mUiBot.assertNoDatasetsEver();
1868 
1869         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1870         // the session started
1871         sReplier.getNextFillRequest();
1872 
1873         // Set credentials...
1874         mActivity.onUsername((v) -> v.setText("malkovich"));
1875         mActivity.onPassword(View::requestFocus);
1876         mActivity.onPassword((v) -> v.setText("malkovich"));
1877 
1878         // ...and login
1879         final String expectedMessage = getWelcomeMessage("malkovich");
1880         final String actualMessage = mActivity.tapLogin();
1881         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1882 
1883         // Assert the snack bar is shown and tap "Save".
1884         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1885 
1886         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1887 
1888         // Assert value of expected fields - should not be sanitized.
1889         final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1890         assertTextAndValue(username, "malkovich");
1891         final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1892         assertTextAndValue(password, "malkovich");
1893     }
1894 
1895     @Test
1896     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveNoRequiredField_NoneFilled()1897     public void testSaveNoRequiredField_NoneFilled() throws Exception {
1898         optionalOnlyTest(FilledFields.NONE);
1899     }
1900 
1901     @Test
1902     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveNoRequiredField_OneFilled()1903     public void testSaveNoRequiredField_OneFilled() throws Exception {
1904         optionalOnlyTest(FilledFields.USERNAME_ONLY);
1905     }
1906 
1907     @Test
1908     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testSaveNoRequiredField_BothFilled()1909     public void testSaveNoRequiredField_BothFilled() throws Exception {
1910         optionalOnlyTest(FilledFields.BOTH);
1911     }
1912 
1913     enum FilledFields {
1914         NONE,
1915         USERNAME_ONLY,
1916         BOTH
1917     }
1918 
optionalOnlyTest(FilledFields filledFields)1919     private void optionalOnlyTest(FilledFields filledFields) throws Exception {
1920         enableService();
1921 
1922         // Set expectations.
1923         sReplier.addResponse(new CannedFillResponse.Builder()
1924                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD)
1925                 .setOptionalSavableIds(ID_USERNAME, ID_PASSWORD)
1926                 .build());
1927 
1928         // Trigger auto-fill.
1929         mActivity.onUsername(View::requestFocus);
1930 
1931         // Validation check.
1932         mUiBot.assertNoDatasetsEver();
1933 
1934         // Wait for onFill() before proceeding, otherwise the fields might be changed before
1935         // the session started
1936         sReplier.getNextFillRequest();
1937 
1938         // Set credentials...
1939         final String expectedUsername;
1940         if (filledFields == FilledFields.USERNAME_ONLY || filledFields == FilledFields.BOTH) {
1941             expectedUsername = BACKDOOR_USERNAME;
1942             mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
1943         } else {
1944             expectedUsername = "";
1945         }
1946         mActivity.onPassword(View::requestFocus);
1947         if (filledFields == FilledFields.BOTH) {
1948             mActivity.onPassword((v) -> v.setText("whatever"));
1949         }
1950 
1951         // ...and login
1952         final String expectedMessage = getWelcomeMessage(expectedUsername);
1953         final String actualMessage = mActivity.tapLogin();
1954         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
1955 
1956         if (filledFields == FilledFields.NONE) {
1957             mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
1958             return;
1959         }
1960 
1961         // Assert the snack bar is shown and tap "Save".
1962         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
1963 
1964         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
1965 
1966         // Assert value of expected fields - should not be sanitized.
1967         final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
1968         assertTextAndValue(username, BACKDOOR_USERNAME);
1969 
1970         if (filledFields == FilledFields.BOTH) {
1971             final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD);
1972             assertTextAndValue(password, "whatever");
1973         }
1974     }
1975 
1976     @Test
1977     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testGenericSave()1978     public void testGenericSave() throws Exception {
1979         customizedSaveTest(SAVE_DATA_TYPE_GENERIC);
1980     }
1981 
1982     @Test
1983     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSavePassword()1984     public void testCustomizedSavePassword() throws Exception {
1985         customizedSaveTest(SAVE_DATA_TYPE_PASSWORD);
1986     }
1987 
1988     @Test
1989     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveAddress()1990     public void testCustomizedSaveAddress() throws Exception {
1991         customizedSaveTest(SAVE_DATA_TYPE_ADDRESS);
1992     }
1993 
1994     @Test
1995     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveCreditCard()1996     public void testCustomizedSaveCreditCard() throws Exception {
1997         customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD);
1998     }
1999 
2000     @Test
2001     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveUsername()2002     public void testCustomizedSaveUsername() throws Exception {
2003         customizedSaveTest(SAVE_DATA_TYPE_USERNAME);
2004     }
2005 
2006     @Test
2007     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveEmailAddress()2008     public void testCustomizedSaveEmailAddress() throws Exception {
2009         customizedSaveTest(SAVE_DATA_TYPE_EMAIL_ADDRESS);
2010     }
2011 
2012     @Test
2013     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveDebitCard()2014     public void testCustomizedSaveDebitCard() throws Exception {
2015         customizedSaveTest(SAVE_DATA_TYPE_DEBIT_CARD);
2016     }
2017 
2018     @Test
2019     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSavePaymentCard()2020     public void testCustomizedSavePaymentCard() throws Exception {
2021         customizedSaveTest(SAVE_DATA_TYPE_PAYMENT_CARD);
2022     }
2023 
2024     @Test
2025     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveGenericCard()2026     public void testCustomizedSaveGenericCard() throws Exception {
2027         customizedSaveTest(SAVE_DATA_TYPE_GENERIC_CARD);
2028     }
2029 
2030     @Test
2031     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveTwoCardTypes()2032     public void testCustomizedSaveTwoCardTypes() throws Exception {
2033         customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD | SAVE_DATA_TYPE_DEBIT_CARD,
2034                 SAVE_DATA_TYPE_GENERIC_CARD);
2035     }
2036 
2037     @Test
2038     @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough")
testCustomizedSaveThreeCardTypes()2039     public void testCustomizedSaveThreeCardTypes() throws Exception {
2040         customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD | SAVE_DATA_TYPE_DEBIT_CARD
2041                 | SAVE_DATA_TYPE_PAYMENT_CARD, SAVE_DATA_TYPE_GENERIC_CARD);
2042     }
2043 
customizedSaveTest(int type)2044     private void customizedSaveTest(int type) throws Exception {
2045         customizedSaveTest(type, type);
2046     }
2047 
customizedSaveTest(int type, int expectedType)2048     private void customizedSaveTest(int type, int expectedType) throws Exception {
2049         // Set service.
2050         enableService();
2051 
2052         // Set expectations.
2053         final String saveDescription = "Your data will be saved with love and care...";
2054         sReplier.addResponse(new CannedFillResponse.Builder()
2055                 .setRequiredSavableIds(type, ID_USERNAME, ID_PASSWORD)
2056                 .setSaveDescription(saveDescription)
2057                 .build());
2058 
2059         // Trigger auto-fill.
2060         mActivity.onUsername(View::requestFocus);
2061 
2062         // Validation check.
2063         mUiBot.assertNoDatasetsEver();
2064 
2065         // Wait for onFill() before proceeding, otherwise the fields might be changed before
2066         // the session started.
2067         sReplier.getNextFillRequest();
2068 
2069         // Set credentials...
2070         mActivity.onUsername((v) -> v.setText("malkovich"));
2071         mActivity.onPassword((v) -> v.setText("malkovich"));
2072 
2073         // ...and login
2074         final String expectedMessage = getWelcomeMessage("malkovich");
2075         final String actualMessage = mActivity.tapLogin();
2076         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
2077 
2078         // Assert the snack bar is shown and tap "Save".
2079         final UiObject2 saveSnackBar = mUiBot.assertSaveShowing(saveDescription, expectedType);
2080         mUiBot.saveForAutofill(saveSnackBar, true);
2081 
2082         // Assert save was called.
2083         sReplier.getNextSaveRequest();
2084     }
2085 
2086     @Presubmit
2087     @Test
testDontTriggerSaveOnFinishWhenRequestedByFlag()2088     public void testDontTriggerSaveOnFinishWhenRequestedByFlag() throws Exception {
2089         enableService();
2090 
2091         // Set expectations.
2092         sReplier.addResponse(new CannedFillResponse.Builder()
2093                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2094                 .setSaveInfoFlags(SaveInfo.FLAG_DONT_SAVE_ON_FINISH)
2095                 .build());
2096 
2097         // Trigger auto-fill.
2098         mActivity.onUsername(View::requestFocus);
2099 
2100         // Validation check.
2101         mUiBot.assertNoDatasetsEver();
2102 
2103         // Wait for onFill() before proceeding, otherwise the fields might be changed before
2104         // the session started
2105         sReplier.getNextFillRequest();
2106 
2107         // Set credentials...
2108         mActivity.onUsername((v) -> v.setText("malkovich"));
2109         mActivity.onPassword((v) -> v.setText("malkovich"));
2110 
2111         // ...and login
2112         final String expectedMessage = getWelcomeMessage("malkovich");
2113         final String actualMessage = mActivity.tapLogin();
2114         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
2115 
2116         // Make sure it didn't trigger save.
2117         mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
2118     }
2119 
2120     @Presubmit
2121     @Test
testAutoFillOneDatasetAndSaveWhenFlagSecure()2122     public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception {
2123         mActivity.setFlags(FLAG_SECURE);
2124         testAutoFillOneDatasetAndSave();
2125     }
2126 
2127     @Test
testAutoFillOneDatasetWhenFlagSecure()2128     public void testAutoFillOneDatasetWhenFlagSecure() throws Exception {
2129         mActivity.setFlags(FLAG_SECURE);
2130         testAutoFillOneDataset();
2131     }
2132 
2133     @Presubmit
2134     @Test
2135     @AppModeFull(reason = "Service-specific test")
testDisableSelf()2136     public void testDisableSelf() throws Exception {
2137         enableService();
2138 
2139         // Can disable while connected.
2140         mActivity.runOnUiThread(() -> mContext.getSystemService(
2141                 AutofillManager.class).disableAutofillServices());
2142 
2143         // Ensure disabled.
2144         assertServiceDisabled();
2145     }
2146 
2147     @Presubmit
2148     @Test
testNeverRejectStyleNegativeSaveButton()2149     public void testNeverRejectStyleNegativeSaveButton() throws Exception {
2150         negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_NEVER);
2151     }
2152 
2153     @Presubmit
2154     @Test
testRejectStyleNegativeSaveButton()2155     public void testRejectStyleNegativeSaveButton() throws Exception {
2156         negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT);
2157     }
2158 
2159     @Test
testCancelStyleNegativeSaveButton()2160     public void testCancelStyleNegativeSaveButton() throws Exception {
2161         negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL);
2162     }
2163 
negativeSaveButtonStyle(int style)2164     private void negativeSaveButtonStyle(int style) throws Exception {
2165         enableService();
2166 
2167         // Set service behavior.
2168 
2169         final String intentAction = "android.autofillservice.cts.CUSTOM_ACTION";
2170 
2171         // Configure the save UI.
2172         final IntentSender listener = PendingIntent.getBroadcast(mContext, 0,
2173                 new Intent(intentAction).setPackage(mContext.getPackageName()),
2174                 PendingIntent.FLAG_IMMUTABLE).getIntentSender();
2175 
2176         sReplier.addResponse(new CannedFillResponse.Builder()
2177                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2178                 .setNegativeAction(style, listener)
2179                 .build());
2180 
2181         // Trigger auto-fill.
2182         mActivity.onUsername(View::requestFocus);
2183 
2184         // Wait for onFill() before proceeding.
2185         sReplier.getNextFillRequest();
2186 
2187         // Trigger save.
2188         mActivity.onUsername((v) -> v.setText("foo"));
2189         mActivity.onPassword((v) -> v.setText("foo"));
2190         mActivity.tapLogin();
2191 
2192         // Start watching for the negative intent
2193         final CountDownLatch latch = new CountDownLatch(1);
2194         final IntentFilter intentFilter = new IntentFilter(intentAction);
2195         mContext.registerReceiver(new BroadcastReceiver() {
2196             @Override
2197             public void onReceive(Context context, Intent intent) {
2198                 mContext.unregisterReceiver(this);
2199                 latch.countDown();
2200             }
2201         }, intentFilter, Context.RECEIVER_NOT_EXPORTED);
2202 
2203         // Trigger the negative button.
2204         mUiBot.saveForAutofill(style, /* yesDoIt= */ false, SAVE_DATA_TYPE_PASSWORD);
2205 
2206         // Wait for the custom action.
2207         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
2208     }
2209 
2210     @Presubmit
2211     @Test
testContinueStylePositiveSaveButton()2212     public void testContinueStylePositiveSaveButton() throws Exception {
2213         enableService();
2214 
2215         // Set service behavior.
2216         sReplier.addResponse(new CannedFillResponse.Builder()
2217                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2218                 .setPositiveAction(SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE)
2219                 .build());
2220 
2221         // Trigger auto-fill.
2222         mActivity.onUsername(View::requestFocus);
2223 
2224         // Wait for onFill() before proceeding.
2225         sReplier.getNextFillRequest();
2226 
2227         // Trigger save.
2228         mActivity.onUsername((v) -> v.setText("foo"));
2229         mActivity.onPassword((v) -> v.setText("foo"));
2230         mActivity.tapLogin();
2231 
2232         // Start watching for the negative intent
2233         // Trigger the negative button.
2234         mUiBot.saveForAutofill(SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE, SAVE_DATA_TYPE_PASSWORD);
2235 
2236         // Assert save was called.
2237         sReplier.getNextSaveRequest();
2238     }
2239 
2240     @Test
2241     @AppModeFull(reason = "Unit test")
testGetTextInputType()2242     public void testGetTextInputType() throws Exception {
2243         // Set service.
2244         enableService();
2245 
2246         // Set expectations.
2247         sReplier.addResponse(NO_RESPONSE);
2248 
2249         // Trigger auto-fill.
2250         mActivity.onUsername(View::requestFocus);
2251 
2252         // Assert input text on fill request:
2253         final FillRequest fillRequest = sReplier.getNextFillRequest();
2254 
2255         final ViewNode label = findNodeByResourceId(fillRequest.structure, ID_PASSWORD_LABEL);
2256         assertThat(label.getInputType()).isEqualTo(TYPE_NULL);
2257         final ViewNode password = findNodeByResourceId(fillRequest.structure, ID_PASSWORD);
2258         assertWithMessage("No TYPE_TEXT_VARIATION_PASSWORD on %s", password.getInputType())
2259                 .that(password.getInputType() & TYPE_TEXT_VARIATION_PASSWORD)
2260                 .isEqualTo(TYPE_TEXT_VARIATION_PASSWORD);
2261     }
2262 
2263     @Test
2264     @AppModeFull(reason = "Unit test")
testNoContainers()2265     public void testNoContainers() throws Exception {
2266 
2267         assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
2268         assumeTrue(
2269                 "Device state is not REAR_DISPLAY",
2270                 !Helper.isDeviceInState(mContext, Helper.DeviceStateEnum.REAR_DISPLAY));
2271 
2272         // Set service.
2273         enableService();
2274 
2275         // Rotation Device (only needed because the flag is set after activity creation)
2276         // Delete this along with flag
2277         mUiBot.setScreenOrientation(LANDSCAPE);
2278         mUiBot.setScreenOrientation(PORTRAIT);
2279         mUiBot.assertShownByRelativeId("username");
2280         mUiBot.selectByRelativeId("username");
2281 
2282         // Set expectations.
2283         sReplier.addResponse(NO_RESPONSE);
2284 
2285         // Trigger auto-fill.
2286         mActivity.onUsername(View::requestFocus);
2287 
2288         mUiBot.assertNoDatasetsEver();
2289 
2290         final FillRequest fillRequest = sReplier.getNextFillRequest();
2291 
2292         // Assert it only has 1 root view with 12 "leaf" nodes:
2293         // 1.text view for app title
2294         // 2.invisible layout
2295         // 3.edit text in the invisible layout
2296         // 4.username text label
2297         // 5.username text field
2298         // 6.password text label
2299         // 7.password text field
2300         // 8.output text field
2301         // 9.clear button
2302         // 10.save button
2303         // 11.login button
2304         // 12.cancel button
2305         //
2306         // But it also has an intermediate container (for username) that should be included because
2307         // it has a resource id.
2308 
2309         // get activity title
2310         final CharSequence activityTitle = mActivity.getPackageName() + "/"
2311                 + getActivityTitle(InstrumentationRegistry.getInstrumentation(), mActivity);
2312         assertNumberOfChildrenWithWindowTitle(fillRequest.structure, 14, activityTitle);
2313 
2314         // Make sure container with a resource id was included:
2315         final ViewNode usernameContainer = findNodeByResourceId(fillRequest.structure,
2316                 ID_USERNAME_CONTAINER);
2317         assertThat(usernameContainer).isNotNull();
2318         assertThat(usernameContainer.getChildCount()).isEqualTo(2);
2319     }
2320 
2321     @Presubmit
2322     @Test
testAutofillManuallyOneDataset()2323     public void testAutofillManuallyOneDataset() throws Exception {
2324         // Set service.
2325         enableService();
2326 
2327         // And activity.
2328         mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
2329         // Set expectations.
2330         sReplier.addResponse(new CannedDataset.Builder()
2331                 .setField(ID_USERNAME, "dude")
2332                 .setField(ID_PASSWORD, "sweet")
2333                 .setPresentation(createPresentation("The Dude"))
2334                 .build());
2335         mActivity.expectAutoFill("dude", "sweet");
2336 
2337         // Explicitly uses the contextual menu to test that functionality.
2338         mUiBot.getAutofillMenuOption(ID_USERNAME).click();
2339 
2340         final FillRequest fillRequest = sReplier.getNextFillRequest();
2341         assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
2342 
2343         // Should have been automatically filled.
2344         mUiBot.selectDataset("The Dude");
2345 
2346         // Check the results.
2347         mActivity.assertAutoFilled();
2348     }
2349 
2350     @Test
2351     @AppModeFull(reason = "testAutoFillOneDataset() is enough")
testAutofillManuallyOneDatasetWhenClipboardFull()2352     public void testAutofillManuallyOneDatasetWhenClipboardFull() throws Exception {
2353         // Set service.
2354         enableService();
2355 
2356         // Set clipboard.
2357         ClipboardManager cm = (ClipboardManager) mActivity.getSystemService(CLIPBOARD_SERVICE);
2358         cm.setPrimaryClip(ClipData.newPlainText(null, "test"));
2359 
2360         // And activity.
2361         mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO));
2362 
2363         // Set expectations.
2364         sReplier.addResponse(new CannedDataset.Builder()
2365                 .setField(ID_USERNAME, "dude")
2366                 .setField(ID_PASSWORD, "sweet")
2367                 .setPresentation(createPresentation("The Dude"))
2368                 .build());
2369         mActivity.expectAutoFill("dude", "sweet");
2370 
2371         // Explicitly uses the contextual menu to test that functionality.
2372         mUiBot.getAutofillMenuOption(ID_USERNAME).click();
2373 
2374         final FillRequest fillRequest = sReplier.getNextFillRequest();
2375         assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
2376 
2377         // Should have been automatically filled.
2378         mUiBot.selectDataset("The Dude");
2379 
2380         // Check the results.
2381         mActivity.assertAutoFilled();
2382 
2383         // clear clipboard
2384         cm.clearPrimaryClip();
2385     }
2386 
2387     @Test
2388     @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough")
testAutofillManuallyTwoDatasetsPickFirst()2389     public void testAutofillManuallyTwoDatasetsPickFirst() throws Exception {
2390         autofillManuallyTwoDatasets(true);
2391     }
2392 
2393     @Test
2394     @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough")
testAutofillManuallyTwoDatasetsPickSecond()2395     public void testAutofillManuallyTwoDatasetsPickSecond() throws Exception {
2396         autofillManuallyTwoDatasets(false);
2397     }
2398 
autofillManuallyTwoDatasets(boolean pickFirst)2399     private void autofillManuallyTwoDatasets(boolean pickFirst) throws Exception {
2400         // Set service.
2401         enableService();
2402 
2403         // Set expectations.
2404         sReplier.addResponse(new CannedFillResponse.Builder()
2405                 .addDataset(new CannedDataset.Builder()
2406                         .setField(ID_USERNAME, "dude")
2407                         .setField(ID_PASSWORD, "sweet")
2408                         .setPresentation(createPresentation("The Dude"))
2409                         .build())
2410                 .addDataset(new CannedDataset.Builder()
2411                         .setField(ID_USERNAME, "jenny")
2412                         .setField(ID_PASSWORD, "8675309")
2413                         .setPresentation(createPresentation("Jenny"))
2414                         .build())
2415                 .build());
2416         if (pickFirst) {
2417             mActivity.expectAutoFill("dude", "sweet");
2418         } else {
2419             mActivity.expectAutoFill("jenny", "8675309");
2420 
2421         }
2422 
2423         // Force a manual autofill request.
2424         mActivity.forceAutofillOnUsername();
2425 
2426         final FillRequest fillRequest = sReplier.getNextFillRequest();
2427         assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
2428 
2429         // Auto-fill it.
2430         final UiObject2 picker = mUiBot.assertDatasets("The Dude", "Jenny");
2431         mUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny");
2432 
2433         // Check the results.
2434         mActivity.assertAutoFilled();
2435     }
2436 
2437     @Test
2438     @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough")
testAutofillManuallyPartialField()2439     public void testAutofillManuallyPartialField() throws Exception {
2440         // Set service.
2441         enableService();
2442 
2443         sReplier.addResponse(NO_RESPONSE);
2444         // And activity.
2445         mActivity.onUsername((v) -> v.setText("dud"));
2446         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2447 
2448         // setText() will trigger a fill request.
2449         // Waits the first fill request triggered by the setText() is received by the service to
2450         // avoid flaky.
2451         sReplier.getNextFillRequest();
2452         mUiBot.waitForIdle();
2453 
2454         // Set expectations.
2455         sReplier.addResponse(new CannedDataset.Builder()
2456                 .setField(ID_USERNAME, "dude")
2457                 .setField(ID_PASSWORD, "sweet")
2458                 .setPresentation(createPresentation("The Dude"))
2459                 .build());
2460         mActivity.expectAutoFill("dude", "sweet");
2461 
2462         // Force a manual autofill request.
2463         mActivity.forceAutofillOnUsername();
2464 
2465         final FillRequest fillRequest = sReplier.getNextFillRequest();
2466         assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST);
2467         // Username value should be available because it triggered the manual request...
2468         assertValue(fillRequest.structure, ID_USERNAME, "dud");
2469         // ... but password didn't
2470         assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
2471 
2472         // Selects the dataset.
2473         mUiBot.selectDataset("The Dude");
2474 
2475         // Check the results.
2476         mActivity.assertAutoFilled();
2477     }
2478 
2479     @Test
2480     @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough")
testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore()2481     public void testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore() throws Exception {
2482         // Set service.
2483         enableService();
2484 
2485         /*
2486          * 1st fill (automatic).
2487          */
2488         // Set expectations.
2489         sReplier.addResponse(new CannedDataset.Builder()
2490                 .setField(ID_USERNAME, "dud")
2491                 .setField(ID_PASSWORD, "sweet")
2492                 .setPresentation(createPresentation("The Dude"))
2493                 .build());
2494         mActivity.expectAutoFill("dud", "sweet");
2495 
2496         // Trigger auto-fill.
2497         requestFocusOnUsername();
2498 
2499         // Assert request.
2500         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
2501         assertThat(fillRequest1.flags).isEqualTo(0);
2502         assertTextIsSanitized(fillRequest1.structure, ID_USERNAME);
2503         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
2504 
2505         // Select it.
2506         mUiBot.selectDataset("The Dude");
2507 
2508         // Check the results.
2509         mActivity.assertAutoFilled();
2510 
2511         /*
2512          * 2nd fill (manual).
2513          */
2514         // Set expectations.
2515         sReplier.addResponse(new CannedDataset.Builder()
2516                 .setField(ID_USERNAME, "DUDE")
2517                 .setField(ID_PASSWORD, "SWEET")
2518                 .setPresentation(createPresentation("THE DUDE"))
2519                 .build());
2520         mActivity.expectAutoFill("DUDE", "SWEET");
2521         // Change password to make sure it's not sent to the service.
2522         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2523 
2524         // Trigger auto-fill.
2525         mActivity.forceAutofillOnUsername();
2526 
2527         // Assert request.
2528         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
2529         assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
2530         assertValue(fillRequest2.structure, ID_USERNAME, "dud");
2531         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
2532 
2533         // Select it.
2534         mUiBot.selectDataset("THE DUDE");
2535 
2536         // Check the results.
2537         mActivity.assertAutoFilled();
2538     }
2539 
2540     @Test
2541     @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough")
testAutofillManuallyAgainAfterManuallyAutofilledBefore()2542     public void testAutofillManuallyAgainAfterManuallyAutofilledBefore() throws Exception {
2543         // Set service.
2544         enableService();
2545 
2546         /*
2547          * 1st fill (manual).
2548          */
2549         // Set expectations.
2550         sReplier.addResponse(new CannedDataset.Builder()
2551                 .setField(ID_USERNAME, "dud")
2552                 .setField(ID_PASSWORD, "sweet")
2553                 .setPresentation(createPresentation("The Dude"))
2554                 .build());
2555         mActivity.expectAutoFill("dud", "sweet");
2556 
2557         // Trigger auto-fill.
2558         mActivity.forceAutofillOnUsername();
2559 
2560         // Assert request.
2561         final FillRequest fillRequest1 = sReplier.getNextFillRequest();
2562         assertHasFlags(fillRequest1.flags, FLAG_MANUAL_REQUEST);
2563         assertValue(fillRequest1.structure, ID_USERNAME, "");
2564         assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD);
2565 
2566         // Select it.
2567         mUiBot.selectDataset("The Dude");
2568 
2569         // Check the results.
2570         mActivity.assertAutoFilled();
2571 
2572         /*
2573          * 2nd fill (manual).
2574          */
2575         // Set expectations.
2576         sReplier.addResponse(new CannedDataset.Builder()
2577                 .setField(ID_USERNAME, "DUDE")
2578                 .setField(ID_PASSWORD, "SWEET")
2579                 .setPresentation(createPresentation("THE DUDE"))
2580                 .build());
2581         mActivity.expectAutoFill("DUDE", "SWEET");
2582         // Change password to make sure it's not sent to the service.
2583         mActivity.onPassword((v) -> v.setText("IamSecretMan"));
2584 
2585         // Trigger auto-fill.
2586         mActivity.forceAutofillOnUsername();
2587 
2588         // Assert request.
2589         final FillRequest fillRequest2 = sReplier.getNextFillRequest();
2590         assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
2591         assertValue(fillRequest2.structure, ID_USERNAME, "dud");
2592         assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD);
2593 
2594         // Select it.
2595         mUiBot.selectDataset("THE DUDE");
2596 
2597         // Check the results.
2598         mActivity.assertAutoFilled();
2599     }
2600 
2601     @FlakyTest(bugId = 162372863) // Re-add @Presubmit after fixing.
2602     @Test
testCommitMultipleTimes()2603     public void testCommitMultipleTimes() throws Throwable {
2604         // Set service.
2605         enableService();
2606 
2607         final CannedFillResponse response = new CannedFillResponse.Builder()
2608                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
2609                 .build();
2610 
2611         for (int i = 1; i <= 10; i++) {
2612             Log.i(TAG, "testCommitMultipleTimes(): step " + i);
2613             final String username = "user-" + i;
2614             final String password = "pass-" + i;
2615             try {
2616                 // Set expectations.
2617                 sReplier.addResponse(response);
2618 
2619                 Timeouts.IDLE_UNBIND_TIMEOUT.run("wait for session created", () -> {
2620                     // Trigger auto-fill.
2621                     mActivity.onUsername(View::clearFocus);
2622                     mActivity.onUsername(View::requestFocus);
2623 
2624                     return isConnected() ? "not_used" : null;
2625                 });
2626 
2627                 sReplier.getNextFillRequest();
2628 
2629                 // Validation check.
2630                 mUiBot.assertNoDatasetsEver();
2631 
2632                 // Set credentials...
2633                 mActivity.onUsername((v) -> v.setText(username));
2634                 mActivity.onPassword((v) -> v.setText(password));
2635 
2636                 // Change focus to prepare for next step - must do it before session is gone
2637                 mActivity.onPassword(View::requestFocus);
2638 
2639                 // ...and save them
2640                 mActivity.tapSave();
2641 
2642                 // Assert the snack bar is shown and tap "Save".
2643                 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
2644 
2645                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
2646 
2647                 // Assert value of expected fields - should not be sanitized.
2648                 final ViewNode usernameNode = findNodeByResourceId(saveRequest.structure,
2649                         ID_USERNAME);
2650                 assertTextAndValue(usernameNode, username);
2651                 final ViewNode passwordNode = findNodeByResourceId(saveRequest.structure,
2652                         ID_PASSWORD);
2653                 assertTextAndValue(passwordNode, password);
2654 
2655                 waitUntilDisconnected();
2656 
2657                 // Wait and check if the save window is correctly hidden.
2658                 mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
2659             } catch (RetryableException e) {
2660                 throw new RetryableException(e, "on step %d", i);
2661             } catch (Throwable t) {
2662                 throw new Throwable("Error on step " + i, t);
2663             }
2664         }
2665     }
2666 
2667     @Presubmit
2668     @Test
testCancelMultipleTimes()2669     public void testCancelMultipleTimes() throws Throwable {
2670         // Set service.
2671         enableService();
2672 
2673         for (int i = 1; i <= 10; i++) {
2674             Log.i(TAG, "testCancelMultipleTimes(): step " + i);
2675             final String username = "user-" + i;
2676             final String password = "pass-" + i;
2677             sReplier.addResponse(new CannedDataset.Builder()
2678                     .setField(ID_USERNAME, username)
2679                     .setField(ID_PASSWORD, password)
2680                     .setPresentation(createPresentation("The Dude"))
2681                     .build());
2682             mActivity.expectAutoFill(username, password);
2683             try {
2684                 // Trigger auto-fill.
2685                 requestFocusOnUsername();
2686 
2687                 waitUntilConnected();
2688                 sReplier.getNextFillRequest();
2689 
2690                 // Auto-fill it.
2691                 mUiBot.selectDataset("The Dude");
2692 
2693                 // Check the results.
2694                 mActivity.assertAutoFilled();
2695 
2696                 // Change focus to prepare for next step - must do it before session is gone
2697                 requestFocusOnPassword();
2698 
2699                 // Rinse and repeat...
2700                 mActivity.tapClear();
2701 
2702                 waitUntilDisconnected();
2703             } catch (RetryableException e) {
2704                 throw e;
2705             } catch (Throwable t) {
2706                 throw new Throwable("Error on step " + i, t);
2707             }
2708         }
2709     }
2710 
2711     @Presubmit
2712     @Test
testClickCustomButton()2713     public void testClickCustomButton() throws Exception {
2714         // Set service.
2715         enableService();
2716 
2717         Intent intent = new Intent(mContext, EmptyActivity.class);
2718         IntentSender sender = PendingIntent.getActivity(mContext, 0, intent,
2719                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT
2720                         | PendingIntent.FLAG_IMMUTABLE).getIntentSender();
2721 
2722         RemoteViews presentation = new RemoteViews(mPackageName, R.layout.list_item);
2723         presentation.setTextViewText(R.id.text1, "Poke");
2724         Intent firstIntent = new Intent(mContext, DummyActivity.class);
2725         presentation.setOnClickPendingIntent(R.id.text1, PendingIntent.getActivity(
2726                 mContext, 0, firstIntent, PendingIntent.FLAG_ONE_SHOT
2727                         | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE));
2728 
2729         // Set expectations.
2730         sReplier.addResponse(new CannedFillResponse.Builder()
2731                 .setAuthentication(sender, ID_USERNAME)
2732                 .setPresentation(presentation)
2733                 .build());
2734 
2735         // Trigger auto-fill.
2736         requestFocusOnUsername();
2737 
2738         // Wait for onFill() before proceeding.
2739         sReplier.getNextFillRequest();
2740 
2741         // Click on the custom button
2742         mUiBot.selectByText("Poke");
2743 
2744         // Make sure the click worked
2745         mUiBot.selectByText("foo");
2746 
2747         // Go back to the filled app.
2748         mUiBot.pressBack();
2749     }
2750 
2751     @Presubmit
2752     @Test
testIsServiceEnabled()2753     public void testIsServiceEnabled() throws Exception {
2754         disableService();
2755         final AutofillManager afm = mActivity.getAutofillManager();
2756         assertThat(afm.hasEnabledAutofillServices()).isFalse();
2757         try {
2758             enableService();
2759             assertThat(afm.hasEnabledAutofillServices()).isTrue();
2760         } finally {
2761             disableService();
2762         }
2763     }
2764 
2765     @Presubmit
2766     @Test
testGetAutofillServiceComponentName()2767     public void testGetAutofillServiceComponentName() throws Exception {
2768         final AutofillManager afm = mActivity.getAutofillManager();
2769 
2770         enableService();
2771         final ComponentName componentName = afm.getAutofillServiceComponentName();
2772         assertThat(componentName.getPackageName()).isEqualTo(SERVICE_PACKAGE);
2773         assertThat(componentName.getClassName()).endsWith(SERVICE_CLASS);
2774 
2775         disableService();
2776         assertThat(afm.getAutofillServiceComponentName()).isNull();
2777     }
2778 
2779     @Presubmit
2780     @Test
testSetupComplete()2781     public void testSetupComplete() throws Exception {
2782         enableService();
2783 
2784         // Validation check.
2785         final AutofillManager afm = mActivity.getAutofillManager();
2786         Helper.assertAutofillEnabled(afm, true);
2787 
2788         // Now disable user_complete and try again.
2789         try {
2790             setUserComplete(false);
2791             Helper.assertAutofillEnabled(afm, false);
2792         } finally {
2793             setUserComplete(true);
2794         }
2795     }
2796 
2797     @Presubmit
2798     @Test
testPopupGoesAwayWhenServiceIsChanged()2799     public void testPopupGoesAwayWhenServiceIsChanged() throws Exception {
2800         // Set service.
2801         enableService();
2802 
2803         // Set expectations.
2804         sReplier.addResponse(new CannedDataset.Builder()
2805                 .setField(ID_USERNAME, "dude")
2806                 .setField(ID_PASSWORD, "sweet")
2807                 .setPresentation(createPresentation("The Dude"))
2808                 .build());
2809         mActivity.expectAutoFill("dude", "sweet");
2810 
2811         // Trigger auto-fill.
2812         requestFocusOnUsername();
2813         sReplier.getNextFillRequest();
2814         mUiBot.assertDatasets("The Dude");
2815 
2816         // Now disable service by setting another service
2817         Helper.enableAutofillService(NoOpAutofillService.SERVICE_NAME);
2818 
2819         // ...and make sure popup's gone
2820         mUiBot.assertNoDatasets();
2821     }
2822 
2823     // TODO(b/70682223): add a new test to make sure service with BIND_AUTOFILL permission works
2824     @Presubmit
2825     @Test
2826     @AppModeFull(reason = "Service-specific test")
testServiceIsDisabledWhenNewServiceInfoIsInvalid()2827     public void testServiceIsDisabledWhenNewServiceInfoIsInvalid() throws Exception {
2828         serviceIsDisabledWhenNewServiceIsInvalid(BadAutofillService.SERVICE_NAME);
2829     }
2830 
2831     @Test
2832     @AppModeFull(reason = "Service-specific test")
testServiceIsDisabledWhenNewServiceNameIsInvalid()2833     public void testServiceIsDisabledWhenNewServiceNameIsInvalid() throws Exception {
2834         serviceIsDisabledWhenNewServiceIsInvalid("Y_U_NO_VALID");
2835     }
2836 
serviceIsDisabledWhenNewServiceIsInvalid(String serviceName)2837     private void serviceIsDisabledWhenNewServiceIsInvalid(String serviceName) throws Exception {
2838         // Set service.
2839         enableService();
2840 
2841         // Set expectations.
2842         sReplier.addResponse(new CannedDataset.Builder()
2843                 .setField(ID_USERNAME, "dude")
2844                 .setField(ID_PASSWORD, "sweet")
2845                 .setPresentation(createPresentation("The Dude"))
2846                 .build());
2847         mActivity.expectAutoFill("dude", "sweet");
2848 
2849         // Trigger autofill.
2850         requestFocusOnUsername();
2851         sReplier.getNextFillRequest();
2852         mUiBot.assertDatasets("The Dude");
2853 
2854         // Now disable service by setting another service...
2855         Helper.enableAutofillService(serviceName);
2856 
2857         // ...and make sure popup's gone
2858         mUiBot.assertNoDatasets();
2859 
2860         // Then try to trigger autofill again...
2861         mActivity.onPassword(View::requestFocus);
2862         //...it should not work!
2863         mUiBot.assertNoDatasetsEver();
2864     }
2865 
2866     @Test
testAutofillMovesCursorToTheEnd()2867     public void testAutofillMovesCursorToTheEnd() throws Exception {
2868         // Set service.
2869         enableService();
2870 
2871         // Set expectations.
2872         sReplier.addResponse(new CannedDataset.Builder()
2873                 .setField(ID_USERNAME, "dude")
2874                 .setField(ID_PASSWORD, "sweet")
2875                 .setPresentation(createPresentation("The Dude"))
2876                 .build());
2877         mActivity.expectAutoFill("dude", "sweet");
2878 
2879         // Trigger auto-fill.
2880         requestFocusOnUsername();
2881         sReplier.getNextFillRequest();
2882 
2883         // Auto-fill it.
2884         mUiBot.selectDataset("The Dude");
2885 
2886         // Check the results.
2887         mActivity.assertAutoFilled();
2888 
2889         // NOTE: need to call getSelectionEnd() inside the UI thread, otherwise it returns 0
2890         final AtomicInteger atomicBombToKillASmallInsect = new AtomicInteger();
2891 
2892         mActivity.onUsername((v) -> atomicBombToKillASmallInsect.set(v.getSelectionEnd()));
2893         assertWithMessage("Wrong position on username").that(atomicBombToKillASmallInsect.get())
2894                 .isEqualTo(4);
2895 
2896         mActivity.onPassword((v) -> atomicBombToKillASmallInsect.set(v.getSelectionEnd()));
2897         assertWithMessage("Wrong position on password").that(atomicBombToKillASmallInsect.get())
2898                 .isEqualTo(5);
2899     }
2900 
2901     @Test
testAutofillLargeNumberOfDatasets()2902     public void testAutofillLargeNumberOfDatasets() throws Exception {
2903         // Set service.
2904         enableService();
2905 
2906         final StringBuilder bigStringBuilder = new StringBuilder();
2907         for (int i = 0; i < 10_000; i++) {
2908             bigStringBuilder.append("BigAmI");
2909         }
2910         final String bigString = bigStringBuilder.toString();
2911 
2912         final int size = 100;
2913         Log.d(TAG, "testAutofillLargeNumberOfDatasets(): " + size + " datasets with "
2914                 + bigString.length() + "-bytes id");
2915 
2916         final CannedFillResponse.Builder response = new CannedFillResponse.Builder();
2917         for (int i = 0; i < size; i++) {
2918             final String suffix = "-" + (i + 1);
2919             response.addDataset(new CannedDataset.Builder()
2920                     .setField(ID_USERNAME, "user" + suffix)
2921                     .setField(ID_PASSWORD, "pass" + suffix)
2922                     .setId(bigString)
2923                     .setPresentation(createPresentation("DS" + suffix))
2924                     .build());
2925         }
2926 
2927         // Set expectations.
2928         sReplier.addResponse(response.build());
2929 
2930         // Trigger auto-fill.
2931         requestFocusOnUsername();
2932         sReplier.getNextFillRequest();
2933 
2934         // Make sure all datasets are shown.
2935         // TODO: improve assertDatasets() so it supports scrolling, and assert all of them are
2936         // shown. In fullscreen there are 4 items, otherwise there are 3 items.
2937         mUiBot.assertDatasetsContains("DS-1", "DS-2", "DS-3");
2938 
2939         // TODO: once it supports scrolling, selects the last dataset and asserts it's filled.
2940     }
2941 
2942     @Presubmit
2943     @Test
testCancellationSignalCalledAfterTimeout()2944     public void testCancellationSignalCalledAfterTimeout() throws Exception {
2945         // Set service.
2946         enableService();
2947 
2948         // Set expectations.
2949         final OneTimeCancellationSignalListener listener =
2950                 new OneTimeCancellationSignalListener(Timeouts.FILL_TIMEOUT.ms() + 2000);
2951         sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
2952 
2953         // Trigger auto-fill.
2954         mActivity.onUsername(View::requestFocus);
2955 
2956         // Attach listener to CancellationSignal.
2957         waitUntilConnected();
2958         sReplier.getNextFillRequest().cancellationSignal.setOnCancelListener(listener);
2959 
2960         // Assert results
2961         listener.assertOnCancelCalled();
2962     }
2963 
2964     @Test
2965     @AppModeFull(reason = "Unit test")
testNewTextAttributes()2966     public void testNewTextAttributes() throws Exception {
2967         enableService();
2968         sReplier.addResponse(NO_RESPONSE);
2969         mActivity.onUsername(View::requestFocus);
2970 
2971         final FillRequest request = sReplier.getNextFillRequest();
2972         final ViewNode username = findNodeByResourceId(request.structure, ID_USERNAME);
2973         assertThat(username.getMinTextEms()).isEqualTo(2);
2974         assertThat(username.getMaxTextEms()).isEqualTo(5);
2975         assertThat(username.getMaxTextLength()).isEqualTo(25);
2976 
2977         final ViewNode container = findNodeByResourceId(request.structure, ID_USERNAME_CONTAINER);
2978         assertThat(container.getMinTextEms()).isEqualTo(-1);
2979         assertThat(container.getMaxTextEms()).isEqualTo(-1);
2980         assertThat(container.getMaxTextLength()).isEqualTo(-1);
2981 
2982         final ViewNode password = findNodeByResourceId(request.structure, ID_PASSWORD);
2983         assertThat(password.getMinTextEms()).isEqualTo(-1);
2984         assertThat(password.getMaxTextEms()).isEqualTo(-1);
2985         assertThat(password.getMaxTextLength()).isEqualTo(5000);
2986     }
2987 
2988     @Test
testUiShowOnChangeAfterAutofill()2989     public void testUiShowOnChangeAfterAutofill() throws Exception {
2990         // Set service.
2991         enableService();
2992 
2993         // Set expectations.
2994         sReplier.addResponse(new CannedDataset.Builder()
2995                 .setField(ID_USERNAME, "dude", createPresentation("dude"))
2996                 .setField(ID_PASSWORD, "sweet", createPresentation("sweet"))
2997                 .build());
2998         mActivity.expectAutoFill("dude", "sweet");
2999 
3000         // Trigger auto-fill.
3001         requestFocusOnUsername();
3002         mUiBot.assertDatasets("dude");
3003         sReplier.getNextFillRequest();
3004         mUiBot.selectDataset("dude");
3005 
3006         // Check the results.
3007         mActivity.assertAutoFilled();
3008         mUiBot.assertNoDatasets();
3009 
3010         // Delete a character.
3011         sendKeyEvent("KEYCODE_DEL");
3012         assertThat(mUiBot.getTextByRelativeId(ID_USERNAME)).isEqualTo("dud");
3013 
3014         mActivity.expectAutoFill("dude", "sweet");
3015 
3016         // Check autofill UI show.
3017         final UiObject2 datasetPicker = mUiBot.assertDatasets("dude");
3018 
3019         // Autofill again.
3020         mUiBot.selectDataset(datasetPicker, "dude");
3021 
3022         // Check the results.
3023         mActivity.assertAutoFilled();
3024         mUiBot.assertNoDatasets();
3025     }
3026 
3027     @Test
testUiShowOnChangeAfterAutofillOnePresentation()3028     public void testUiShowOnChangeAfterAutofillOnePresentation() throws Exception {
3029         // Set service.
3030         enableService();
3031 
3032         // Set expectations.
3033         sReplier.addResponse(new CannedDataset.Builder()
3034                 .setField(ID_USERNAME, "dude")
3035                 .setField(ID_PASSWORD, "sweet")
3036                 .setPresentation(createPresentation("The Dude"))
3037                 .build());
3038         mActivity.expectAutoFill("dude", "sweet");
3039 
3040         // Trigger auto-fill.
3041         requestFocusOnUsername();
3042         mUiBot.assertDatasets("The Dude");
3043         sReplier.getNextFillRequest();
3044         mUiBot.selectDataset("The Dude");
3045 
3046         // Check the results.
3047         mActivity.assertAutoFilled();
3048         mUiBot.assertNoDatasets();
3049 
3050         // Delete username
3051         mUiBot.clearTextByRelativeId(ID_USERNAME);
3052 
3053         mActivity.expectAutoFill("dude", "sweet");
3054 
3055         // Check autofill UI show.
3056         final UiObject2 datasetPicker = mUiBot.assertDatasets("The Dude");
3057 
3058         // Autofill again.
3059         mUiBot.selectDataset(datasetPicker, "The Dude");
3060 
3061         // Check the results.
3062         mActivity.assertAutoFilled();
3063         mUiBot.assertNoDatasets();
3064     }
3065 
3066     @Presubmit
3067     @Test
testCancelActionButton()3068     public void testCancelActionButton() throws Exception {
3069         // Set service.
3070         enableService();
3071 
3072         // Set expectations.
3073         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
3074                 .addDataset(new CannedDataset.Builder()
3075                         .setField(ID_USERNAME, "dude")
3076                         .setField(ID_PASSWORD, "sweet")
3077                         .setPresentation(createPresentationWithCancel("The Dude"))
3078                         .build())
3079                 .setPresentationCancelIds(new int[]{R.id.cancel_fill});
3080         sReplier.addResponse(builder.build());
3081 
3082         // Trigger auto-fill.
3083         mActivity.onUsername(View::requestFocus);
3084         sReplier.getNextFillRequest();
3085 
3086         mUiBot.assertDatasetsContains("The Dude");
3087 
3088         // Tap cancel button on fill UI
3089         mUiBot.selectByRelativeId(ID_CANCEL_FILL);
3090         mUiBot.waitForIdle();
3091 
3092         mUiBot.assertNoDatasets();
3093 
3094         // Test and verify auto-fill does not trigger
3095         mActivity.onPassword(View::requestFocus);
3096         mUiBot.waitForIdle();
3097 
3098         mUiBot.assertNoDatasetsEver();
3099 
3100         // Test and verify auto-fill does not trigger.
3101         mActivity.onUsername(View::requestFocus);
3102         mUiBot.waitForIdle();
3103 
3104         mUiBot.assertNoDatasetsEver();
3105 
3106         // Reset
3107         mActivity.tapClear();
3108 
3109         // Set expectations.
3110         final CannedFillResponse.Builder builder2 = new CannedFillResponse.Builder()
3111                 .addDataset(new CannedDataset.Builder()
3112                         .setField(ID_USERNAME, "dude")
3113                         .setField(ID_PASSWORD, "sweet")
3114                         .setPresentation(createPresentationWithCancel("The Dude"))
3115                         .build())
3116                 .setPresentationCancelIds(new int[]{R.id.cancel});
3117         sReplier.addResponse(builder2.build());
3118 
3119         // Trigger auto-fill.
3120         mActivity.onPassword(View::requestFocus);
3121         sReplier.getNextFillRequest();
3122 
3123         // Verify auto-fill has been triggered.
3124         mUiBot.assertDatasetsContains("The Dude");
3125     }
3126 
3127     @Presubmit
3128     @Test
3129     @AppModeFull(reason = "WRITE_SECURE_SETTING permission can't be grant to instant apps")
testSwitchInputMethod_noNewFillRequest()3130     public void testSwitchInputMethod_noNewFillRequest() throws Exception {
3131         // TODO(b/187664861): Find better solution for small display device.
3132         mUiBot.assumeMinimumResolution(500);
3133 	mUiBot.setScreenResolution();
3134         // Set service
3135         enableService();
3136 
3137         // Set expectations
3138         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
3139                 .addDataset(new CannedDataset.Builder()
3140                         .setField(ID_USERNAME, "dude")
3141                         .setField(ID_PASSWORD, "sweet")
3142                         .setPresentation(createPresentation("The Dude"))
3143                         .build());
3144         sReplier.addResponse(builder.build());
3145 
3146         // Trigger auto-fill
3147         mActivity.onUsername(View::requestFocus);
3148         sReplier.getNextFillRequest();
3149 
3150         mUiBot.assertDatasetsContains("The Dude");
3151 
3152         // Trigger IME switch event
3153         Helper.mockSwitchInputMethod(sContext);
3154         mUiBot.waitForIdleSync();
3155 
3156         // Tap password field
3157         mUiBot.selectByRelativeId(ID_PASSWORD);
3158         mUiBot.waitForIdleSync();
3159 
3160         mUiBot.assertDatasetsContains("The Dude");
3161 
3162         // No new fill request
3163         sReplier.assertNoUnhandledFillRequests();
3164         mUiBot.resetScreenResolution();
3165     }
3166 }
3167