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