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