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