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