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