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 windowManager.removeView(overlay[0]); 1264 }); 1265 } catch (Exception e) { 1266 mSafeCleanerRule.add(e); 1267 } 1268 } 1269 } 1270 1271 @Test 1272 @AppModeFull(reason = "testAutoFillOneDataset() is enough") testAutoFillMultipleDatasetsPickFirst()1273 public void testAutoFillMultipleDatasetsPickFirst() throws Exception { 1274 multipleDatasetsTest(1); 1275 } 1276 1277 @Test 1278 @AppModeFull(reason = "testAutoFillOneDataset() is enough") testAutoFillMultipleDatasetsPickSecond()1279 public void testAutoFillMultipleDatasetsPickSecond() throws Exception { 1280 multipleDatasetsTest(2); 1281 } 1282 1283 @Test 1284 @AppModeFull(reason = "testAutoFillOneDataset() is enough") testAutoFillMultipleDatasetsPickThird()1285 public void testAutoFillMultipleDatasetsPickThird() throws Exception { 1286 multipleDatasetsTest(3); 1287 } 1288 multipleDatasetsTest(int number)1289 private void multipleDatasetsTest(int number) throws Exception { 1290 // Set service. 1291 enableService(); 1292 1293 // Set expectations. 1294 sReplier.addResponse(new CannedFillResponse.Builder() 1295 .addDataset(new CannedDataset.Builder() 1296 .setField(ID_USERNAME, "mr_plow") 1297 .setField(ID_PASSWORD, "D'OH!") 1298 .setPresentation(createPresentation("Mr Plow")) 1299 .build()) 1300 .addDataset(new CannedDataset.Builder() 1301 .setField(ID_USERNAME, "el barto") 1302 .setField(ID_PASSWORD, "aycaramba!") 1303 .setPresentation(createPresentation("El Barto")) 1304 .build()) 1305 .addDataset(new CannedDataset.Builder() 1306 .setField(ID_USERNAME, "mr sparkle") 1307 .setField(ID_PASSWORD, "Aw3someP0wer") 1308 .setPresentation(createPresentation("Mr Sparkle")) 1309 .build()) 1310 .build()); 1311 final String name; 1312 1313 switch (number) { 1314 case 1: 1315 name = "Mr Plow"; 1316 mActivity.expectAutoFill("mr_plow", "D'OH!"); 1317 break; 1318 case 2: 1319 name = "El Barto"; 1320 mActivity.expectAutoFill("el barto", "aycaramba!"); 1321 break; 1322 case 3: 1323 name = "Mr Sparkle"; 1324 mActivity.expectAutoFill("mr sparkle", "Aw3someP0wer"); 1325 break; 1326 default: 1327 throw new IllegalArgumentException("invalid dataset number: " + number); 1328 } 1329 1330 // Trigger auto-fill. 1331 requestFocusOnUsername(); 1332 sReplier.getNextFillRequest(); 1333 1334 // Make sure all datasets are shown. 1335 final UiObject2 picker = mUiBot.assertDatasets("Mr Plow", "El Barto", "Mr Sparkle"); 1336 1337 // Auto-fill it. 1338 mUiBot.selectDataset(picker, name); 1339 1340 // Check the results. 1341 mActivity.assertAutoFilled(); 1342 } 1343 1344 /** 1345 * Tests the scenario where the service uses custom remote views for different fields (username 1346 * and password). 1347 */ 1348 @Presubmit 1349 @Test testAutofillOneDatasetCustomPresentation()1350 public void testAutofillOneDatasetCustomPresentation() throws Exception { 1351 // Set service. 1352 enableService(); 1353 1354 // Set expectations. 1355 sReplier.addResponse(new CannedDataset.Builder() 1356 .setField(ID_USERNAME, "dude", 1357 createPresentation("The Dude")) 1358 .setField(ID_PASSWORD, "sweet", 1359 createPresentation("Dude's password")) 1360 .build()); 1361 mActivity.expectAutoFill("dude", "sweet"); 1362 1363 // Trigger auto-fill. 1364 requestFocusOnUsername(); 1365 sReplier.getNextFillRequest(); 1366 1367 // Check initial field. 1368 mUiBot.assertDatasets("The Dude"); 1369 1370 // Then move around... 1371 requestFocusOnPassword(); 1372 mUiBot.assertDatasets("Dude's password"); 1373 requestFocusOnUsername(); 1374 mUiBot.assertDatasets("The Dude"); 1375 1376 // Auto-fill it. 1377 requestFocusOnPassword(); 1378 mUiBot.selectDataset("Dude's password"); 1379 1380 // Check the results. 1381 mActivity.assertAutoFilled(); 1382 } 1383 1384 /** 1385 * Tests the scenario where the service uses custom remote views for different fields (username 1386 * and password) and the dataset itself, and each dataset has the same number of fields. 1387 */ 1388 @Test 1389 @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough") testAutofillMultipleDatasetsCustomPresentations()1390 public void testAutofillMultipleDatasetsCustomPresentations() throws Exception { 1391 // Set service. 1392 enableService(); 1393 1394 // Set expectations. 1395 sReplier.addResponse(new CannedFillResponse.Builder() 1396 .addDataset(new CannedDataset.Builder(createPresentation("Dataset1")) 1397 .setField(ID_USERNAME, "user1") // no presentation 1398 .setField(ID_PASSWORD, "pass1", createPresentation("Pass1")) 1399 .build()) 1400 .addDataset(new CannedDataset.Builder() 1401 .setField(ID_USERNAME, "user2", createPresentation("User2")) 1402 .setField(ID_PASSWORD, "pass2") // no presentation 1403 .setPresentation(createPresentation("Dataset2")) 1404 .build()) 1405 .build()); 1406 mActivity.expectAutoFill("user1", "pass1"); 1407 1408 // Trigger auto-fill. 1409 requestFocusOnUsername(); 1410 sReplier.getNextFillRequest(); 1411 1412 // Check initial field. 1413 mUiBot.assertDatasets("Dataset1", "User2"); 1414 1415 // Then move around... 1416 requestFocusOnPassword(); 1417 mUiBot.assertDatasets("Pass1", "Dataset2"); 1418 requestFocusOnUsername(); 1419 mUiBot.assertDatasets("Dataset1", "User2"); 1420 1421 // Auto-fill it. 1422 requestFocusOnPassword(); 1423 mUiBot.selectDataset("Pass1"); 1424 1425 // Check the results. 1426 mActivity.assertAutoFilled(); 1427 } 1428 1429 /** 1430 * Tests the scenario where the service uses custom remote views for different fields (username 1431 * and password), and each dataset has the same number of fields. 1432 */ 1433 @Test 1434 @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough") testAutofillMultipleDatasetsCustomPresentationSameFields()1435 public void testAutofillMultipleDatasetsCustomPresentationSameFields() throws Exception { 1436 // Set service. 1437 enableService(); 1438 1439 // Set expectations. 1440 sReplier.addResponse(new CannedFillResponse.Builder() 1441 .addDataset(new CannedDataset.Builder() 1442 .setField(ID_USERNAME, "user1", createPresentation("User1")) 1443 .setField(ID_PASSWORD, "pass1", createPresentation("Pass1")) 1444 .build()) 1445 .addDataset(new CannedDataset.Builder() 1446 .setField(ID_USERNAME, "user2", createPresentation("User2")) 1447 .setField(ID_PASSWORD, "pass2", createPresentation("Pass2")) 1448 .build()) 1449 .build()); 1450 mActivity.expectAutoFill("user1", "pass1"); 1451 1452 // Trigger auto-fill. 1453 requestFocusOnUsername(); 1454 sReplier.getNextFillRequest(); 1455 1456 // Check initial field. 1457 mUiBot.assertDatasets("User1", "User2"); 1458 1459 // Then move around... 1460 requestFocusOnPassword(); 1461 mUiBot.assertDatasets("Pass1", "Pass2"); 1462 requestFocusOnUsername(); 1463 mUiBot.assertDatasets("User1", "User2"); 1464 1465 // Auto-fill it. 1466 requestFocusOnPassword(); 1467 mUiBot.selectDataset("Pass1"); 1468 1469 // Check the results. 1470 mActivity.assertAutoFilled(); 1471 } 1472 1473 /** 1474 * Tests the scenario where the service uses custom remote views for different fields (username 1475 * and password), but each dataset has a different number of fields. 1476 */ 1477 @Test 1478 @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough") testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()1479 public void testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField() 1480 throws Exception { 1481 // Set service. 1482 enableService(); 1483 1484 // Set expectations. 1485 sReplier.addResponse(new CannedFillResponse.Builder() 1486 .addDataset(new CannedDataset.Builder() 1487 .setField(ID_USERNAME, "user1", createPresentation("User1")) 1488 .build()) 1489 .addDataset(new CannedDataset.Builder() 1490 .setField(ID_USERNAME, "user2", createPresentation("User2")) 1491 .setField(ID_PASSWORD, "pass2", createPresentation("Pass2")) 1492 .build()) 1493 .build()); 1494 mActivity.expectAutoFill("user2", "pass2"); 1495 1496 // Trigger auto-fill. 1497 requestFocusOnUsername(); 1498 sReplier.getNextFillRequest(); 1499 1500 // Check initial field. 1501 mUiBot.assertDatasets("User1", "User2"); 1502 1503 // Then move around... 1504 requestFocusOnPassword(); 1505 mUiBot.assertDatasets("Pass2"); 1506 requestFocusOnUsername(); 1507 mUiBot.assertDatasets("User1", "User2"); 1508 1509 // Auto-fill it. 1510 mUiBot.selectDataset("User2"); 1511 1512 // Check the results. 1513 mActivity.assertAutoFilled(); 1514 } 1515 1516 @Test 1517 @AsbSecurityTest(cveBugId = 281533566) remoteViews_doesNotSpillAcrossUsers()1518 public void remoteViews_doesNotSpillAcrossUsers() throws Exception { 1519 // Set service. 1520 enableService(); 1521 1522 1523 RemoteViews firstRv = createPresentation("hello"); 1524 RemoteViews secondRv = createPresentation("world"); 1525 1526 // bad url, should not be displayed 1527 firstRv.setImageViewIcon(R.id.icon, 1528 Icon.createWithContentUri("content://1000@com.android.contacts/display_photo/1")); 1529 secondRv.setImageViewIcon(R.id.icon, 1530 Icon.createWithContentUri("content://1000@com.android.contacts/display_photo/1")); 1531 1532 // Set expectations. 1533 sReplier.addResponse(new CannedFillResponse.Builder() 1534 .addDataset(new CannedDataset.Builder() 1535 .setField(ID_USERNAME, "dude", firstRv) 1536 .setField(ID_PASSWORD, "sweet", secondRv) 1537 .build()) 1538 .setHeader(firstRv) 1539 .setFooter(secondRv) 1540 .build()); 1541 1542 mActivity.expectAutoFill("dude", "sweet"); 1543 1544 // Trigger auto-fill. 1545 mActivity.onUsername(View::requestFocus); 1546 sReplier.getNextFillRequest(); 1547 1548 // Assert that the dataset is not shown 1549 assertThrows(RetryableException.class, 1550 () -> mUiBot.assertDatasets("The Dude")); 1551 1552 // Assert that header/footer is not shown 1553 assertThrows(RetryableException.class, 1554 () -> mUiBot.assertDatasetsWithBorders("hello", "world", "The Dude")); 1555 } 1556 1557 /** 1558 * Tests the scenario where the service uses custom remote views for different fields (username 1559 * and password), but each dataset has a different number of fields. 1560 */ 1561 @Test 1562 @AppModeFull(reason = "testAutofillOneDatasetCustomPresentation() is enough") testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()1563 public void testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField() 1564 throws Exception { 1565 // Set service. 1566 enableService(); 1567 1568 // Set expectations. 1569 sReplier.addResponse(new CannedFillResponse.Builder() 1570 .addDataset(new CannedDataset.Builder() 1571 .setField(ID_USERNAME, "user1", createPresentation("User1")) 1572 .setField(ID_PASSWORD, "pass1", createPresentation("Pass1")) 1573 .build()) 1574 .addDataset(new CannedDataset.Builder() 1575 .setField(ID_PASSWORD, "pass2", createPresentation("Pass2")) 1576 .build()) 1577 .build()); 1578 mActivity.expectAutoFill("user1", "pass1"); 1579 1580 // Trigger auto-fill. 1581 requestFocusOnUsername(); 1582 sReplier.getNextFillRequest(); 1583 1584 // Check initial field. 1585 mUiBot.assertDatasets("User1"); 1586 1587 // Then move around... 1588 requestFocusOnPassword(); 1589 mUiBot.assertDatasets("Pass1", "Pass2"); 1590 requestFocusOnUsername(); 1591 mUiBot.assertDatasets("User1"); 1592 1593 // Auto-fill it. 1594 mUiBot.selectDataset("User1"); 1595 1596 // Check the results. 1597 mActivity.assertAutoFilled(); 1598 } 1599 1600 @Test 1601 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnly()1602 public void testSaveOnly() throws Exception { 1603 saveOnlyTest(false); 1604 } 1605 1606 @Test 1607 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnlyTriggeredManually()1608 public void testSaveOnlyTriggeredManually() throws Exception { 1609 saveOnlyTest(false); 1610 } 1611 saveOnlyTest(boolean manually)1612 private void saveOnlyTest(boolean manually) throws Exception { 1613 enableService(); 1614 1615 // Set expectations. 1616 sReplier.addResponse(new CannedFillResponse.Builder() 1617 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 1618 .build()); 1619 1620 // Trigger auto-fill. 1621 if (manually) { 1622 mActivity.forceAutofillOnUsername(); 1623 } else { 1624 mActivity.onUsername(View::requestFocus); 1625 } 1626 1627 // Validation check. 1628 mUiBot.assertNoDatasetsEver(); 1629 1630 // Wait for onFill() before proceeding, otherwise the fields might be changed before 1631 // the session started 1632 sReplier.getNextFillRequest(); 1633 1634 // Set credentials... 1635 mActivity.onUsername((v) -> v.setText("malkovich")); 1636 mActivity.onPassword((v) -> v.setText("malkovich")); 1637 1638 // ...and login 1639 final String expectedMessage = getWelcomeMessage("malkovich"); 1640 final String actualMessage = mActivity.tapLogin(); 1641 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1642 1643 // Assert the snack bar is shown and tap "Save". 1644 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 1645 1646 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 1647 sReplier.assertNoUnhandledSaveRequests(); 1648 assertThat(saveRequest.datasetIds).isNull(); 1649 1650 // Assert value of expected fields - should not be sanitized. 1651 try { 1652 final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME); 1653 assertTextAndValue(username, "malkovich"); 1654 final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD); 1655 assertTextAndValue(password, "malkovich"); 1656 } catch (AssertionError | RuntimeException e) { 1657 dumpStructure("saveOnlyTest() failed", saveRequest.structure); 1658 throw e; 1659 } 1660 } 1661 1662 @Test testSaveGoesAwayWhenTappingHomeButton()1663 public void testSaveGoesAwayWhenTappingHomeButton() throws Exception { 1664 saveGoesAway(DismissType.HOME_BUTTON); 1665 } 1666 1667 @Test testSaveGoesAwayWhenTappingBackButton()1668 public void testSaveGoesAwayWhenTappingBackButton() throws Exception { 1669 saveGoesAway(DismissType.BACK_BUTTON); 1670 } 1671 1672 @Test testSaveGoesAwayWhenTouchingOutside()1673 public void testSaveGoesAwayWhenTouchingOutside() throws Exception { 1674 saveGoesAway(DismissType.TOUCH_OUTSIDE); 1675 } 1676 saveGoesAway(DismissType dismissType)1677 private void saveGoesAway(DismissType dismissType) throws Exception { 1678 enableService(); 1679 1680 // Set expectations. 1681 sReplier.addResponse(new CannedFillResponse.Builder() 1682 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 1683 .build()); 1684 1685 // Trigger auto-fill. 1686 mActivity.onUsername(View::requestFocus); 1687 1688 // Validation check. 1689 mUiBot.assertNoDatasetsEver(); 1690 1691 // Wait for onFill() before proceeding, otherwise the fields might be changed before 1692 // the session started 1693 sReplier.getNextFillRequest(); 1694 1695 // Set credentials... 1696 mActivity.onUsername((v) -> v.setText("malkovich")); 1697 mActivity.onPassword((v) -> v.setText("malkovich")); 1698 1699 // ...and login 1700 final String expectedMessage = getWelcomeMessage("malkovich"); 1701 final String actualMessage = mActivity.tapLogin(); 1702 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1703 1704 // Assert the snack bar is shown and tap "Save". 1705 mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD); 1706 1707 // Then make sure it goes away when user doesn't want it.. 1708 String when; 1709 switch (dismissType) { 1710 case BACK_BUTTON: 1711 when = "back button tapped"; 1712 mUiBot.pressBack(); 1713 break; 1714 case HOME_BUTTON: 1715 when = "home button tapped"; 1716 mUiBot.pressHome(); 1717 break; 1718 case TOUCH_OUTSIDE: 1719 when = "touched outside"; 1720 mUiBot.touchOutsideSaveDialog(); 1721 break; 1722 default: 1723 throw new IllegalArgumentException("invalid dismiss type: " + dismissType); 1724 } 1725 mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD, when); 1726 } 1727 1728 @Test 1729 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnlyPreFilled()1730 public void testSaveOnlyPreFilled() throws Exception { 1731 saveOnlyTestPreFilled(false); 1732 } 1733 1734 @Test 1735 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnlyTriggeredManuallyPreFilled()1736 public void testSaveOnlyTriggeredManuallyPreFilled() throws Exception { 1737 saveOnlyTestPreFilled(true); 1738 } 1739 saveOnlyTestPreFilled(boolean manually)1740 private void saveOnlyTestPreFilled(boolean manually) throws Exception { 1741 enableService(); 1742 1743 // Set expectations. 1744 sReplier.addResponse(new CannedFillResponse.Builder() 1745 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 1746 .build()); 1747 1748 // Set activity 1749 mActivity.onUsername((v) -> v.setText("user_before")); 1750 mActivity.onPassword((v) -> v.setText("pass_before")); 1751 1752 // Trigger auto-fill. 1753 if (manually) { 1754 // setText() will trigger a fill request. 1755 // Waits the first fill request triggered by the setText() is received by the service to 1756 // avoid flaky. 1757 sReplier.getNextFillRequest(); 1758 mUiBot.waitForIdle(); 1759 1760 // Set expectations again. 1761 sReplier.addResponse(new CannedFillResponse.Builder() 1762 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 1763 .build()); 1764 mActivity.forceAutofillOnUsername(); 1765 } else { 1766 mUiBot.selectByRelativeId(ID_USERNAME); 1767 } 1768 mUiBot.waitForIdle(); 1769 1770 // Validation check. 1771 mUiBot.assertNoDatasetsEver(); 1772 1773 // Wait for onFill() before proceeding, otherwise the fields might be changed before 1774 // the session started 1775 sReplier.getNextFillRequest(); 1776 1777 // Set credentials... 1778 mActivity.onUsername((v) -> v.setText("user_after")); 1779 mActivity.onPassword((v) -> v.setText("pass_after")); 1780 1781 // ...and login 1782 final String expectedMessage = getWelcomeMessage("user_after"); 1783 final String actualMessage = mActivity.tapLogin(); 1784 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1785 mUiBot.waitForIdle(); 1786 1787 // Assert the snack bar is shown and tap "Save". 1788 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 1789 1790 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 1791 sReplier.assertNoUnhandledSaveRequests(); 1792 1793 // Assert value of expected fields - should not be sanitized. 1794 try { 1795 final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME); 1796 assertTextAndValue(username, "user_after"); 1797 final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD); 1798 assertTextAndValue(password, "pass_after"); 1799 } catch (AssertionError | RuntimeException e) { 1800 dumpStructure("saveOnlyTest() failed", saveRequest.structure); 1801 throw e; 1802 } 1803 } 1804 1805 @Test 1806 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnlyTwoRequiredFieldsOnePrefilled()1807 public void testSaveOnlyTwoRequiredFieldsOnePrefilled() throws Exception { 1808 enableService(); 1809 1810 // Set expectations. 1811 sReplier.addResponse(new CannedFillResponse.Builder() 1812 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 1813 .build()); 1814 1815 // Set activity 1816 mActivity.onUsername((v) -> v.setText("I_AM_USER")); 1817 1818 // Trigger auto-fill. 1819 mActivity.onPassword(View::requestFocus); 1820 1821 // Wait for onFill() before changing value, otherwise the fields might be changed before 1822 // the session started 1823 sReplier.getNextFillRequest(); 1824 mUiBot.assertNoDatasetsEver(); 1825 1826 // Set credentials... 1827 mActivity.onPassword((v) -> v.setText("thou should pass")); // contains pass 1828 1829 // ...and login 1830 final String expectedMessage = getWelcomeMessage("I_AM_USER"); // contains pass 1831 final String actualMessage = mActivity.tapLogin(); 1832 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1833 1834 // Assert the snack bar is shown and tap "Save". 1835 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 1836 1837 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 1838 sReplier.assertNoUnhandledSaveRequests(); 1839 1840 // Assert value of expected fields - should not be sanitized. 1841 try { 1842 final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME); 1843 assertTextAndValue(username, "I_AM_USER"); 1844 final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD); 1845 assertTextAndValue(password, "thou should pass"); 1846 } catch (AssertionError | RuntimeException e) { 1847 dumpStructure("saveOnlyTest() failed", saveRequest.structure); 1848 throw e; 1849 } 1850 } 1851 1852 @Test 1853 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveOnlyOptionalField()1854 public void testSaveOnlyOptionalField() throws Exception { 1855 enableService(); 1856 1857 // Set expectations. 1858 sReplier.addResponse(new CannedFillResponse.Builder() 1859 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME) 1860 .setOptionalSavableIds(ID_PASSWORD) 1861 .build()); 1862 1863 // Trigger auto-fill. 1864 mActivity.onUsername(View::requestFocus); 1865 1866 // Validation check. 1867 mUiBot.assertNoDatasetsEver(); 1868 1869 // Wait for onFill() before proceeding, otherwise the fields might be changed before 1870 // the session started 1871 sReplier.getNextFillRequest(); 1872 1873 // Set credentials... 1874 mActivity.onUsername((v) -> v.setText("malkovich")); 1875 mActivity.onPassword(View::requestFocus); 1876 mActivity.onPassword((v) -> v.setText("malkovich")); 1877 1878 // ...and login 1879 final String expectedMessage = getWelcomeMessage("malkovich"); 1880 final String actualMessage = mActivity.tapLogin(); 1881 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1882 1883 // Assert the snack bar is shown and tap "Save". 1884 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 1885 1886 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 1887 1888 // Assert value of expected fields - should not be sanitized. 1889 final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME); 1890 assertTextAndValue(username, "malkovich"); 1891 final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD); 1892 assertTextAndValue(password, "malkovich"); 1893 } 1894 1895 @Test 1896 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveNoRequiredField_NoneFilled()1897 public void testSaveNoRequiredField_NoneFilled() throws Exception { 1898 optionalOnlyTest(FilledFields.NONE); 1899 } 1900 1901 @Test 1902 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveNoRequiredField_OneFilled()1903 public void testSaveNoRequiredField_OneFilled() throws Exception { 1904 optionalOnlyTest(FilledFields.USERNAME_ONLY); 1905 } 1906 1907 @Test 1908 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testSaveNoRequiredField_BothFilled()1909 public void testSaveNoRequiredField_BothFilled() throws Exception { 1910 optionalOnlyTest(FilledFields.BOTH); 1911 } 1912 1913 enum FilledFields { 1914 NONE, 1915 USERNAME_ONLY, 1916 BOTH 1917 } 1918 optionalOnlyTest(FilledFields filledFields)1919 private void optionalOnlyTest(FilledFields filledFields) throws Exception { 1920 enableService(); 1921 1922 // Set expectations. 1923 sReplier.addResponse(new CannedFillResponse.Builder() 1924 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD) 1925 .setOptionalSavableIds(ID_USERNAME, ID_PASSWORD) 1926 .build()); 1927 1928 // Trigger auto-fill. 1929 mActivity.onUsername(View::requestFocus); 1930 1931 // Validation check. 1932 mUiBot.assertNoDatasetsEver(); 1933 1934 // Wait for onFill() before proceeding, otherwise the fields might be changed before 1935 // the session started 1936 sReplier.getNextFillRequest(); 1937 1938 // Set credentials... 1939 final String expectedUsername; 1940 if (filledFields == FilledFields.USERNAME_ONLY || filledFields == FilledFields.BOTH) { 1941 expectedUsername = BACKDOOR_USERNAME; 1942 mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME)); 1943 } else { 1944 expectedUsername = ""; 1945 } 1946 mActivity.onPassword(View::requestFocus); 1947 if (filledFields == FilledFields.BOTH) { 1948 mActivity.onPassword((v) -> v.setText("whatever")); 1949 } 1950 1951 // ...and login 1952 final String expectedMessage = getWelcomeMessage(expectedUsername); 1953 final String actualMessage = mActivity.tapLogin(); 1954 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 1955 1956 if (filledFields == FilledFields.NONE) { 1957 mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); 1958 return; 1959 } 1960 1961 // Assert the snack bar is shown and tap "Save". 1962 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 1963 1964 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 1965 1966 // Assert value of expected fields - should not be sanitized. 1967 final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME); 1968 assertTextAndValue(username, BACKDOOR_USERNAME); 1969 1970 if (filledFields == FilledFields.BOTH) { 1971 final ViewNode password = findNodeByResourceId(saveRequest.structure, ID_PASSWORD); 1972 assertTextAndValue(password, "whatever"); 1973 } 1974 } 1975 1976 @Test 1977 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testGenericSave()1978 public void testGenericSave() throws Exception { 1979 customizedSaveTest(SAVE_DATA_TYPE_GENERIC); 1980 } 1981 1982 @Test 1983 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSavePassword()1984 public void testCustomizedSavePassword() throws Exception { 1985 customizedSaveTest(SAVE_DATA_TYPE_PASSWORD); 1986 } 1987 1988 @Test 1989 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveAddress()1990 public void testCustomizedSaveAddress() throws Exception { 1991 customizedSaveTest(SAVE_DATA_TYPE_ADDRESS); 1992 } 1993 1994 @Test 1995 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveCreditCard()1996 public void testCustomizedSaveCreditCard() throws Exception { 1997 customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD); 1998 } 1999 2000 @Test 2001 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveUsername()2002 public void testCustomizedSaveUsername() throws Exception { 2003 customizedSaveTest(SAVE_DATA_TYPE_USERNAME); 2004 } 2005 2006 @Test 2007 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveEmailAddress()2008 public void testCustomizedSaveEmailAddress() throws Exception { 2009 customizedSaveTest(SAVE_DATA_TYPE_EMAIL_ADDRESS); 2010 } 2011 2012 @Test 2013 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveDebitCard()2014 public void testCustomizedSaveDebitCard() throws Exception { 2015 customizedSaveTest(SAVE_DATA_TYPE_DEBIT_CARD); 2016 } 2017 2018 @Test 2019 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSavePaymentCard()2020 public void testCustomizedSavePaymentCard() throws Exception { 2021 customizedSaveTest(SAVE_DATA_TYPE_PAYMENT_CARD); 2022 } 2023 2024 @Test 2025 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveGenericCard()2026 public void testCustomizedSaveGenericCard() throws Exception { 2027 customizedSaveTest(SAVE_DATA_TYPE_GENERIC_CARD); 2028 } 2029 2030 @Test 2031 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveTwoCardTypes()2032 public void testCustomizedSaveTwoCardTypes() throws Exception { 2033 customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD | SAVE_DATA_TYPE_DEBIT_CARD, 2034 SAVE_DATA_TYPE_GENERIC_CARD); 2035 } 2036 2037 @Test 2038 @AppModeFull(reason = "testAutoFillOneDatasetAndSave() is enough") testCustomizedSaveThreeCardTypes()2039 public void testCustomizedSaveThreeCardTypes() throws Exception { 2040 customizedSaveTest(SAVE_DATA_TYPE_CREDIT_CARD | SAVE_DATA_TYPE_DEBIT_CARD 2041 | SAVE_DATA_TYPE_PAYMENT_CARD, SAVE_DATA_TYPE_GENERIC_CARD); 2042 } 2043 customizedSaveTest(int type)2044 private void customizedSaveTest(int type) throws Exception { 2045 customizedSaveTest(type, type); 2046 } 2047 customizedSaveTest(int type, int expectedType)2048 private void customizedSaveTest(int type, int expectedType) throws Exception { 2049 // Set service. 2050 enableService(); 2051 2052 // Set expectations. 2053 final String saveDescription = "Your data will be saved with love and care..."; 2054 sReplier.addResponse(new CannedFillResponse.Builder() 2055 .setRequiredSavableIds(type, ID_USERNAME, ID_PASSWORD) 2056 .setSaveDescription(saveDescription) 2057 .build()); 2058 2059 // Trigger auto-fill. 2060 mActivity.onUsername(View::requestFocus); 2061 2062 // Validation check. 2063 mUiBot.assertNoDatasetsEver(); 2064 2065 // Wait for onFill() before proceeding, otherwise the fields might be changed before 2066 // the session started. 2067 sReplier.getNextFillRequest(); 2068 2069 // Set credentials... 2070 mActivity.onUsername((v) -> v.setText("malkovich")); 2071 mActivity.onPassword((v) -> v.setText("malkovich")); 2072 2073 // ...and login 2074 final String expectedMessage = getWelcomeMessage("malkovich"); 2075 final String actualMessage = mActivity.tapLogin(); 2076 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 2077 2078 // Assert the snack bar is shown and tap "Save". 2079 final UiObject2 saveSnackBar = mUiBot.assertSaveShowing(saveDescription, expectedType); 2080 mUiBot.saveForAutofill(saveSnackBar, true); 2081 2082 // Assert save was called. 2083 sReplier.getNextSaveRequest(); 2084 } 2085 2086 @Presubmit 2087 @Test testDontTriggerSaveOnFinishWhenRequestedByFlag()2088 public void testDontTriggerSaveOnFinishWhenRequestedByFlag() throws Exception { 2089 enableService(); 2090 2091 // Set expectations. 2092 sReplier.addResponse(new CannedFillResponse.Builder() 2093 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 2094 .setSaveInfoFlags(SaveInfo.FLAG_DONT_SAVE_ON_FINISH) 2095 .build()); 2096 2097 // Trigger auto-fill. 2098 mActivity.onUsername(View::requestFocus); 2099 2100 // Validation check. 2101 mUiBot.assertNoDatasetsEver(); 2102 2103 // Wait for onFill() before proceeding, otherwise the fields might be changed before 2104 // the session started 2105 sReplier.getNextFillRequest(); 2106 2107 // Set credentials... 2108 mActivity.onUsername((v) -> v.setText("malkovich")); 2109 mActivity.onPassword((v) -> v.setText("malkovich")); 2110 2111 // ...and login 2112 final String expectedMessage = getWelcomeMessage("malkovich"); 2113 final String actualMessage = mActivity.tapLogin(); 2114 assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage); 2115 2116 // Make sure it didn't trigger save. 2117 mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); 2118 } 2119 2120 @Presubmit 2121 @Test testAutoFillOneDatasetAndSaveWhenFlagSecure()2122 public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception { 2123 mActivity.setFlags(FLAG_SECURE); 2124 testAutoFillOneDatasetAndSave(); 2125 } 2126 2127 @Test testAutoFillOneDatasetWhenFlagSecure()2128 public void testAutoFillOneDatasetWhenFlagSecure() throws Exception { 2129 mActivity.setFlags(FLAG_SECURE); 2130 testAutoFillOneDataset(); 2131 } 2132 2133 @Presubmit 2134 @Test 2135 @AppModeFull(reason = "Service-specific test") testDisableSelf()2136 public void testDisableSelf() throws Exception { 2137 enableService(); 2138 2139 // Can disable while connected. 2140 mActivity.runOnUiThread(() -> mContext.getSystemService( 2141 AutofillManager.class).disableAutofillServices()); 2142 2143 // Ensure disabled. 2144 assertServiceDisabled(); 2145 } 2146 2147 @Presubmit 2148 @Test testNeverRejectStyleNegativeSaveButton()2149 public void testNeverRejectStyleNegativeSaveButton() throws Exception { 2150 negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_NEVER); 2151 } 2152 2153 @Presubmit 2154 @Test testRejectStyleNegativeSaveButton()2155 public void testRejectStyleNegativeSaveButton() throws Exception { 2156 negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT); 2157 } 2158 2159 @Test testCancelStyleNegativeSaveButton()2160 public void testCancelStyleNegativeSaveButton() throws Exception { 2161 negativeSaveButtonStyle(SaveInfo.NEGATIVE_BUTTON_STYLE_CANCEL); 2162 } 2163 negativeSaveButtonStyle(int style)2164 private void negativeSaveButtonStyle(int style) throws Exception { 2165 enableService(); 2166 2167 // Set service behavior. 2168 2169 final String intentAction = "android.autofillservice.cts.CUSTOM_ACTION"; 2170 2171 // Configure the save UI. 2172 final IntentSender listener = PendingIntent.getBroadcast(mContext, 0, 2173 new Intent(intentAction).setPackage(mContext.getPackageName()), 2174 PendingIntent.FLAG_IMMUTABLE).getIntentSender(); 2175 2176 sReplier.addResponse(new CannedFillResponse.Builder() 2177 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 2178 .setNegativeAction(style, listener) 2179 .build()); 2180 2181 // Trigger auto-fill. 2182 mActivity.onUsername(View::requestFocus); 2183 2184 // Wait for onFill() before proceeding. 2185 sReplier.getNextFillRequest(); 2186 2187 // Trigger save. 2188 mActivity.onUsername((v) -> v.setText("foo")); 2189 mActivity.onPassword((v) -> v.setText("foo")); 2190 mActivity.tapLogin(); 2191 2192 // Start watching for the negative intent 2193 final CountDownLatch latch = new CountDownLatch(1); 2194 final IntentFilter intentFilter = new IntentFilter(intentAction); 2195 mContext.registerReceiver(new BroadcastReceiver() { 2196 @Override 2197 public void onReceive(Context context, Intent intent) { 2198 mContext.unregisterReceiver(this); 2199 latch.countDown(); 2200 } 2201 }, intentFilter, Context.RECEIVER_NOT_EXPORTED); 2202 2203 // Trigger the negative button. 2204 mUiBot.saveForAutofill(style, /* yesDoIt= */ false, SAVE_DATA_TYPE_PASSWORD); 2205 2206 // Wait for the custom action. 2207 assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue(); 2208 } 2209 2210 @Presubmit 2211 @Test testContinueStylePositiveSaveButton()2212 public void testContinueStylePositiveSaveButton() throws Exception { 2213 enableService(); 2214 2215 // Set service behavior. 2216 sReplier.addResponse(new CannedFillResponse.Builder() 2217 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 2218 .setPositiveAction(SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE) 2219 .build()); 2220 2221 // Trigger auto-fill. 2222 mActivity.onUsername(View::requestFocus); 2223 2224 // Wait for onFill() before proceeding. 2225 sReplier.getNextFillRequest(); 2226 2227 // Trigger save. 2228 mActivity.onUsername((v) -> v.setText("foo")); 2229 mActivity.onPassword((v) -> v.setText("foo")); 2230 mActivity.tapLogin(); 2231 2232 // Start watching for the negative intent 2233 // Trigger the negative button. 2234 mUiBot.saveForAutofill(SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE, SAVE_DATA_TYPE_PASSWORD); 2235 2236 // Assert save was called. 2237 sReplier.getNextSaveRequest(); 2238 } 2239 2240 @Test 2241 @AppModeFull(reason = "Unit test") testGetTextInputType()2242 public void testGetTextInputType() throws Exception { 2243 // Set service. 2244 enableService(); 2245 2246 // Set expectations. 2247 sReplier.addResponse(NO_RESPONSE); 2248 2249 // Trigger auto-fill. 2250 mActivity.onUsername(View::requestFocus); 2251 2252 // Assert input text on fill request: 2253 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2254 2255 final ViewNode label = findNodeByResourceId(fillRequest.structure, ID_PASSWORD_LABEL); 2256 assertThat(label.getInputType()).isEqualTo(TYPE_NULL); 2257 final ViewNode password = findNodeByResourceId(fillRequest.structure, ID_PASSWORD); 2258 assertWithMessage("No TYPE_TEXT_VARIATION_PASSWORD on %s", password.getInputType()) 2259 .that(password.getInputType() & TYPE_TEXT_VARIATION_PASSWORD) 2260 .isEqualTo(TYPE_TEXT_VARIATION_PASSWORD); 2261 } 2262 2263 @Test 2264 @AppModeFull(reason = "Unit test") testNoContainers()2265 public void testNoContainers() throws Exception { 2266 2267 assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext)); 2268 assumeTrue( 2269 "Device state is not REAR_DISPLAY", 2270 !Helper.isDeviceInState(mContext, Helper.DeviceStateEnum.REAR_DISPLAY)); 2271 2272 // Set service. 2273 enableService(); 2274 2275 // Rotation Device (only needed because the flag is set after activity creation) 2276 // Delete this along with flag 2277 mUiBot.setScreenOrientation(LANDSCAPE); 2278 mUiBot.setScreenOrientation(PORTRAIT); 2279 mUiBot.assertShownByRelativeId("username"); 2280 mUiBot.selectByRelativeId("username"); 2281 2282 // Set expectations. 2283 sReplier.addResponse(NO_RESPONSE); 2284 2285 // Trigger auto-fill. 2286 mActivity.onUsername(View::requestFocus); 2287 2288 mUiBot.assertNoDatasetsEver(); 2289 2290 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2291 2292 // Assert it only has 1 root view with 12 "leaf" nodes: 2293 // 1.text view for app title 2294 // 2.invisible layout 2295 // 3.edit text in the invisible layout 2296 // 4.username text label 2297 // 5.username text field 2298 // 6.password text label 2299 // 7.password text field 2300 // 8.output text field 2301 // 9.clear button 2302 // 10.save button 2303 // 11.login button 2304 // 12.cancel button 2305 // 2306 // But it also has an intermediate container (for username) that should be included because 2307 // it has a resource id. 2308 2309 // get activity title 2310 final CharSequence activityTitle = mActivity.getPackageName() + "/" 2311 + getActivityTitle(InstrumentationRegistry.getInstrumentation(), mActivity); 2312 assertNumberOfChildrenWithWindowTitle(fillRequest.structure, 14, activityTitle); 2313 2314 // Make sure container with a resource id was included: 2315 final ViewNode usernameContainer = findNodeByResourceId(fillRequest.structure, 2316 ID_USERNAME_CONTAINER); 2317 assertThat(usernameContainer).isNotNull(); 2318 assertThat(usernameContainer.getChildCount()).isEqualTo(2); 2319 } 2320 2321 @Presubmit 2322 @Test testAutofillManuallyOneDataset()2323 public void testAutofillManuallyOneDataset() throws Exception { 2324 // Set service. 2325 enableService(); 2326 2327 // And activity. 2328 mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO)); 2329 // Set expectations. 2330 sReplier.addResponse(new CannedDataset.Builder() 2331 .setField(ID_USERNAME, "dude") 2332 .setField(ID_PASSWORD, "sweet") 2333 .setPresentation(createPresentation("The Dude")) 2334 .build()); 2335 mActivity.expectAutoFill("dude", "sweet"); 2336 2337 // Explicitly uses the contextual menu to test that functionality. 2338 mUiBot.getAutofillMenuOption(ID_USERNAME).click(); 2339 2340 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2341 assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST); 2342 2343 // Should have been automatically filled. 2344 mUiBot.selectDataset("The Dude"); 2345 2346 // Check the results. 2347 mActivity.assertAutoFilled(); 2348 } 2349 2350 @Test 2351 @AppModeFull(reason = "testAutoFillOneDataset() is enough") testAutofillManuallyOneDatasetWhenClipboardFull()2352 public void testAutofillManuallyOneDatasetWhenClipboardFull() throws Exception { 2353 // Set service. 2354 enableService(); 2355 2356 // Set clipboard. 2357 ClipboardManager cm = (ClipboardManager) mActivity.getSystemService(CLIPBOARD_SERVICE); 2358 cm.setPrimaryClip(ClipData.newPlainText(null, "test")); 2359 2360 // And activity. 2361 mActivity.onUsername((v) -> v.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO)); 2362 2363 // Set expectations. 2364 sReplier.addResponse(new CannedDataset.Builder() 2365 .setField(ID_USERNAME, "dude") 2366 .setField(ID_PASSWORD, "sweet") 2367 .setPresentation(createPresentation("The Dude")) 2368 .build()); 2369 mActivity.expectAutoFill("dude", "sweet"); 2370 2371 // Explicitly uses the contextual menu to test that functionality. 2372 mUiBot.getAutofillMenuOption(ID_USERNAME).click(); 2373 2374 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2375 assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST); 2376 2377 // Should have been automatically filled. 2378 mUiBot.selectDataset("The Dude"); 2379 2380 // Check the results. 2381 mActivity.assertAutoFilled(); 2382 2383 // clear clipboard 2384 cm.clearPrimaryClip(); 2385 } 2386 2387 @Test 2388 @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough") testAutofillManuallyTwoDatasetsPickFirst()2389 public void testAutofillManuallyTwoDatasetsPickFirst() throws Exception { 2390 autofillManuallyTwoDatasets(true); 2391 } 2392 2393 @Test 2394 @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough") testAutofillManuallyTwoDatasetsPickSecond()2395 public void testAutofillManuallyTwoDatasetsPickSecond() throws Exception { 2396 autofillManuallyTwoDatasets(false); 2397 } 2398 autofillManuallyTwoDatasets(boolean pickFirst)2399 private void autofillManuallyTwoDatasets(boolean pickFirst) throws Exception { 2400 // Set service. 2401 enableService(); 2402 2403 // Set expectations. 2404 sReplier.addResponse(new CannedFillResponse.Builder() 2405 .addDataset(new CannedDataset.Builder() 2406 .setField(ID_USERNAME, "dude") 2407 .setField(ID_PASSWORD, "sweet") 2408 .setPresentation(createPresentation("The Dude")) 2409 .build()) 2410 .addDataset(new CannedDataset.Builder() 2411 .setField(ID_USERNAME, "jenny") 2412 .setField(ID_PASSWORD, "8675309") 2413 .setPresentation(createPresentation("Jenny")) 2414 .build()) 2415 .build()); 2416 if (pickFirst) { 2417 mActivity.expectAutoFill("dude", "sweet"); 2418 } else { 2419 mActivity.expectAutoFill("jenny", "8675309"); 2420 2421 } 2422 2423 // Force a manual autofill request. 2424 mActivity.forceAutofillOnUsername(); 2425 2426 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2427 assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST); 2428 2429 // Auto-fill it. 2430 final UiObject2 picker = mUiBot.assertDatasets("The Dude", "Jenny"); 2431 mUiBot.selectDataset(picker, pickFirst ? "The Dude" : "Jenny"); 2432 2433 // Check the results. 2434 mActivity.assertAutoFilled(); 2435 } 2436 2437 @Test 2438 @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough") testAutofillManuallyPartialField()2439 public void testAutofillManuallyPartialField() throws Exception { 2440 // Set service. 2441 enableService(); 2442 2443 sReplier.addResponse(NO_RESPONSE); 2444 // And activity. 2445 mActivity.onUsername((v) -> v.setText("dud")); 2446 mActivity.onPassword((v) -> v.setText("IamSecretMan")); 2447 2448 // setText() will trigger a fill request. 2449 // Waits the first fill request triggered by the setText() is received by the service to 2450 // avoid flaky. 2451 sReplier.getNextFillRequest(); 2452 mUiBot.waitForIdle(); 2453 2454 // Set expectations. 2455 sReplier.addResponse(new CannedDataset.Builder() 2456 .setField(ID_USERNAME, "dude") 2457 .setField(ID_PASSWORD, "sweet") 2458 .setPresentation(createPresentation("The Dude")) 2459 .build()); 2460 mActivity.expectAutoFill("dude", "sweet"); 2461 2462 // Force a manual autofill request. 2463 mActivity.forceAutofillOnUsername(); 2464 2465 final FillRequest fillRequest = sReplier.getNextFillRequest(); 2466 assertHasFlags(fillRequest.flags, FLAG_MANUAL_REQUEST); 2467 // Username value should be available because it triggered the manual request... 2468 assertValue(fillRequest.structure, ID_USERNAME, "dud"); 2469 // ... but password didn't 2470 assertTextIsSanitized(fillRequest.structure, ID_PASSWORD); 2471 2472 // Selects the dataset. 2473 mUiBot.selectDataset("The Dude"); 2474 2475 // Check the results. 2476 mActivity.assertAutoFilled(); 2477 } 2478 2479 @Test 2480 @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough") testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore()2481 public void testAutofillManuallyAgainAfterAutomaticallyAutofilledBefore() throws Exception { 2482 // Set service. 2483 enableService(); 2484 2485 /* 2486 * 1st fill (automatic). 2487 */ 2488 // Set expectations. 2489 sReplier.addResponse(new CannedDataset.Builder() 2490 .setField(ID_USERNAME, "dud") 2491 .setField(ID_PASSWORD, "sweet") 2492 .setPresentation(createPresentation("The Dude")) 2493 .build()); 2494 mActivity.expectAutoFill("dud", "sweet"); 2495 2496 // Trigger auto-fill. 2497 requestFocusOnUsername(); 2498 2499 // Assert request. 2500 final FillRequest fillRequest1 = sReplier.getNextFillRequest(); 2501 assertThat(fillRequest1.flags).isEqualTo(0); 2502 assertTextIsSanitized(fillRequest1.structure, ID_USERNAME); 2503 assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD); 2504 2505 // Select it. 2506 mUiBot.selectDataset("The Dude"); 2507 2508 // Check the results. 2509 mActivity.assertAutoFilled(); 2510 2511 /* 2512 * 2nd fill (manual). 2513 */ 2514 // Set expectations. 2515 sReplier.addResponse(new CannedDataset.Builder() 2516 .setField(ID_USERNAME, "DUDE") 2517 .setField(ID_PASSWORD, "SWEET") 2518 .setPresentation(createPresentation("THE DUDE")) 2519 .build()); 2520 mActivity.expectAutoFill("DUDE", "SWEET"); 2521 // Change password to make sure it's not sent to the service. 2522 mActivity.onPassword((v) -> v.setText("IamSecretMan")); 2523 2524 // Trigger auto-fill. 2525 mActivity.forceAutofillOnUsername(); 2526 2527 // Assert request. 2528 final FillRequest fillRequest2 = sReplier.getNextFillRequest(); 2529 assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST); 2530 assertValue(fillRequest2.structure, ID_USERNAME, "dud"); 2531 assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD); 2532 2533 // Select it. 2534 mUiBot.selectDataset("THE DUDE"); 2535 2536 // Check the results. 2537 mActivity.assertAutoFilled(); 2538 } 2539 2540 @Test 2541 @AppModeFull(reason = "testAutofillManuallyOneDataset() is enough") testAutofillManuallyAgainAfterManuallyAutofilledBefore()2542 public void testAutofillManuallyAgainAfterManuallyAutofilledBefore() throws Exception { 2543 // Set service. 2544 enableService(); 2545 2546 /* 2547 * 1st fill (manual). 2548 */ 2549 // Set expectations. 2550 sReplier.addResponse(new CannedDataset.Builder() 2551 .setField(ID_USERNAME, "dud") 2552 .setField(ID_PASSWORD, "sweet") 2553 .setPresentation(createPresentation("The Dude")) 2554 .build()); 2555 mActivity.expectAutoFill("dud", "sweet"); 2556 2557 // Trigger auto-fill. 2558 mActivity.forceAutofillOnUsername(); 2559 2560 // Assert request. 2561 final FillRequest fillRequest1 = sReplier.getNextFillRequest(); 2562 assertHasFlags(fillRequest1.flags, FLAG_MANUAL_REQUEST); 2563 assertValue(fillRequest1.structure, ID_USERNAME, ""); 2564 assertTextIsSanitized(fillRequest1.structure, ID_PASSWORD); 2565 2566 // Select it. 2567 mUiBot.selectDataset("The Dude"); 2568 2569 // Check the results. 2570 mActivity.assertAutoFilled(); 2571 2572 /* 2573 * 2nd fill (manual). 2574 */ 2575 // Set expectations. 2576 sReplier.addResponse(new CannedDataset.Builder() 2577 .setField(ID_USERNAME, "DUDE") 2578 .setField(ID_PASSWORD, "SWEET") 2579 .setPresentation(createPresentation("THE DUDE")) 2580 .build()); 2581 mActivity.expectAutoFill("DUDE", "SWEET"); 2582 // Change password to make sure it's not sent to the service. 2583 mActivity.onPassword((v) -> v.setText("IamSecretMan")); 2584 2585 // Trigger auto-fill. 2586 mActivity.forceAutofillOnUsername(); 2587 2588 // Assert request. 2589 final FillRequest fillRequest2 = sReplier.getNextFillRequest(); 2590 assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST); 2591 assertValue(fillRequest2.structure, ID_USERNAME, "dud"); 2592 assertTextIsSanitized(fillRequest2.structure, ID_PASSWORD); 2593 2594 // Select it. 2595 mUiBot.selectDataset("THE DUDE"); 2596 2597 // Check the results. 2598 mActivity.assertAutoFilled(); 2599 } 2600 2601 @FlakyTest(bugId = 162372863) // Re-add @Presubmit after fixing. 2602 @Test testCommitMultipleTimes()2603 public void testCommitMultipleTimes() throws Throwable { 2604 // Set service. 2605 enableService(); 2606 2607 final CannedFillResponse response = new CannedFillResponse.Builder() 2608 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD) 2609 .build(); 2610 2611 for (int i = 1; i <= 10; i++) { 2612 Log.i(TAG, "testCommitMultipleTimes(): step " + i); 2613 final String username = "user-" + i; 2614 final String password = "pass-" + i; 2615 try { 2616 // Set expectations. 2617 sReplier.addResponse(response); 2618 2619 Timeouts.IDLE_UNBIND_TIMEOUT.run("wait for session created", () -> { 2620 // Trigger auto-fill. 2621 mActivity.onUsername(View::clearFocus); 2622 mActivity.onUsername(View::requestFocus); 2623 2624 return isConnected() ? "not_used" : null; 2625 }); 2626 2627 sReplier.getNextFillRequest(); 2628 2629 // Validation check. 2630 mUiBot.assertNoDatasetsEver(); 2631 2632 // Set credentials... 2633 mActivity.onUsername((v) -> v.setText(username)); 2634 mActivity.onPassword((v) -> v.setText(password)); 2635 2636 // Change focus to prepare for next step - must do it before session is gone 2637 mActivity.onPassword(View::requestFocus); 2638 2639 // ...and save them 2640 mActivity.tapSave(); 2641 2642 // Assert the snack bar is shown and tap "Save". 2643 mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 2644 2645 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 2646 2647 // Assert value of expected fields - should not be sanitized. 2648 final ViewNode usernameNode = findNodeByResourceId(saveRequest.structure, 2649 ID_USERNAME); 2650 assertTextAndValue(usernameNode, username); 2651 final ViewNode passwordNode = findNodeByResourceId(saveRequest.structure, 2652 ID_PASSWORD); 2653 assertTextAndValue(passwordNode, password); 2654 2655 waitUntilDisconnected(); 2656 2657 // Wait and check if the save window is correctly hidden. 2658 mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD); 2659 } catch (RetryableException e) { 2660 throw new RetryableException(e, "on step %d", i); 2661 } catch (Throwable t) { 2662 throw new Throwable("Error on step " + i, t); 2663 } 2664 } 2665 } 2666 2667 @Presubmit 2668 @Test testCancelMultipleTimes()2669 public void testCancelMultipleTimes() throws Throwable { 2670 // Set service. 2671 enableService(); 2672 2673 for (int i = 1; i <= 10; i++) { 2674 Log.i(TAG, "testCancelMultipleTimes(): step " + i); 2675 final String username = "user-" + i; 2676 final String password = "pass-" + i; 2677 sReplier.addResponse(new CannedDataset.Builder() 2678 .setField(ID_USERNAME, username) 2679 .setField(ID_PASSWORD, password) 2680 .setPresentation(createPresentation("The Dude")) 2681 .build()); 2682 mActivity.expectAutoFill(username, password); 2683 try { 2684 // Trigger auto-fill. 2685 requestFocusOnUsername(); 2686 2687 waitUntilConnected(); 2688 sReplier.getNextFillRequest(); 2689 2690 // Auto-fill it. 2691 mUiBot.selectDataset("The Dude"); 2692 2693 // Check the results. 2694 mActivity.assertAutoFilled(); 2695 2696 // Change focus to prepare for next step - must do it before session is gone 2697 requestFocusOnPassword(); 2698 2699 // Rinse and repeat... 2700 mActivity.tapClear(); 2701 2702 waitUntilDisconnected(); 2703 } catch (RetryableException e) { 2704 throw e; 2705 } catch (Throwable t) { 2706 throw new Throwable("Error on step " + i, t); 2707 } 2708 } 2709 } 2710 2711 @Presubmit 2712 @Test testClickCustomButton()2713 public void testClickCustomButton() throws Exception { 2714 // Set service. 2715 enableService(); 2716 2717 Intent intent = new Intent(mContext, EmptyActivity.class); 2718 IntentSender sender = PendingIntent.getActivity(mContext, 0, intent, 2719 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT 2720 | PendingIntent.FLAG_IMMUTABLE).getIntentSender(); 2721 2722 RemoteViews presentation = new RemoteViews(mPackageName, R.layout.list_item); 2723 presentation.setTextViewText(R.id.text1, "Poke"); 2724 Intent firstIntent = new Intent(mContext, DummyActivity.class); 2725 presentation.setOnClickPendingIntent(R.id.text1, PendingIntent.getActivity( 2726 mContext, 0, firstIntent, PendingIntent.FLAG_ONE_SHOT 2727 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE)); 2728 2729 // Set expectations. 2730 sReplier.addResponse(new CannedFillResponse.Builder() 2731 .setAuthentication(sender, ID_USERNAME) 2732 .setPresentation(presentation) 2733 .build()); 2734 2735 // Trigger auto-fill. 2736 requestFocusOnUsername(); 2737 2738 // Wait for onFill() before proceeding. 2739 sReplier.getNextFillRequest(); 2740 2741 // Click on the custom button 2742 mUiBot.selectByText("Poke"); 2743 2744 // Make sure the click worked 2745 mUiBot.selectByText("foo"); 2746 2747 // Go back to the filled app. 2748 mUiBot.pressBack(); 2749 } 2750 2751 @Presubmit 2752 @Test testIsServiceEnabled()2753 public void testIsServiceEnabled() throws Exception { 2754 disableService(); 2755 final AutofillManager afm = mActivity.getAutofillManager(); 2756 assertThat(afm.hasEnabledAutofillServices()).isFalse(); 2757 try { 2758 enableService(); 2759 assertThat(afm.hasEnabledAutofillServices()).isTrue(); 2760 } finally { 2761 disableService(); 2762 } 2763 } 2764 2765 @Presubmit 2766 @Test testGetAutofillServiceComponentName()2767 public void testGetAutofillServiceComponentName() throws Exception { 2768 final AutofillManager afm = mActivity.getAutofillManager(); 2769 2770 enableService(); 2771 final ComponentName componentName = afm.getAutofillServiceComponentName(); 2772 assertThat(componentName.getPackageName()).isEqualTo(SERVICE_PACKAGE); 2773 assertThat(componentName.getClassName()).endsWith(SERVICE_CLASS); 2774 2775 disableService(); 2776 assertThat(afm.getAutofillServiceComponentName()).isNull(); 2777 } 2778 2779 @Presubmit 2780 @Test testSetupComplete()2781 public void testSetupComplete() throws Exception { 2782 enableService(); 2783 2784 // Validation check. 2785 final AutofillManager afm = mActivity.getAutofillManager(); 2786 Helper.assertAutofillEnabled(afm, true); 2787 2788 // Now disable user_complete and try again. 2789 try { 2790 setUserComplete(false); 2791 Helper.assertAutofillEnabled(afm, false); 2792 } finally { 2793 setUserComplete(true); 2794 } 2795 } 2796 2797 @Presubmit 2798 @Test testPopupGoesAwayWhenServiceIsChanged()2799 public void testPopupGoesAwayWhenServiceIsChanged() throws Exception { 2800 // Set service. 2801 enableService(); 2802 2803 // Set expectations. 2804 sReplier.addResponse(new CannedDataset.Builder() 2805 .setField(ID_USERNAME, "dude") 2806 .setField(ID_PASSWORD, "sweet") 2807 .setPresentation(createPresentation("The Dude")) 2808 .build()); 2809 mActivity.expectAutoFill("dude", "sweet"); 2810 2811 // Trigger auto-fill. 2812 requestFocusOnUsername(); 2813 sReplier.getNextFillRequest(); 2814 mUiBot.assertDatasets("The Dude"); 2815 2816 // Now disable service by setting another service 2817 Helper.enableAutofillService(NoOpAutofillService.SERVICE_NAME); 2818 2819 // ...and make sure popup's gone 2820 mUiBot.assertNoDatasets(); 2821 } 2822 2823 // TODO(b/70682223): add a new test to make sure service with BIND_AUTOFILL permission works 2824 @Presubmit 2825 @Test 2826 @AppModeFull(reason = "Service-specific test") testServiceIsDisabledWhenNewServiceInfoIsInvalid()2827 public void testServiceIsDisabledWhenNewServiceInfoIsInvalid() throws Exception { 2828 serviceIsDisabledWhenNewServiceIsInvalid(BadAutofillService.SERVICE_NAME); 2829 } 2830 2831 @Test 2832 @AppModeFull(reason = "Service-specific test") testServiceIsDisabledWhenNewServiceNameIsInvalid()2833 public void testServiceIsDisabledWhenNewServiceNameIsInvalid() throws Exception { 2834 serviceIsDisabledWhenNewServiceIsInvalid("Y_U_NO_VALID"); 2835 } 2836 serviceIsDisabledWhenNewServiceIsInvalid(String serviceName)2837 private void serviceIsDisabledWhenNewServiceIsInvalid(String serviceName) throws Exception { 2838 // Set service. 2839 enableService(); 2840 2841 // Set expectations. 2842 sReplier.addResponse(new CannedDataset.Builder() 2843 .setField(ID_USERNAME, "dude") 2844 .setField(ID_PASSWORD, "sweet") 2845 .setPresentation(createPresentation("The Dude")) 2846 .build()); 2847 mActivity.expectAutoFill("dude", "sweet"); 2848 2849 // Trigger autofill. 2850 requestFocusOnUsername(); 2851 sReplier.getNextFillRequest(); 2852 mUiBot.assertDatasets("The Dude"); 2853 2854 // Now disable service by setting another service... 2855 Helper.enableAutofillService(serviceName); 2856 2857 // ...and make sure popup's gone 2858 mUiBot.assertNoDatasets(); 2859 2860 // Then try to trigger autofill again... 2861 mActivity.onPassword(View::requestFocus); 2862 //...it should not work! 2863 mUiBot.assertNoDatasetsEver(); 2864 } 2865 2866 @Test testAutofillMovesCursorToTheEnd()2867 public void testAutofillMovesCursorToTheEnd() throws Exception { 2868 // Set service. 2869 enableService(); 2870 2871 // Set expectations. 2872 sReplier.addResponse(new CannedDataset.Builder() 2873 .setField(ID_USERNAME, "dude") 2874 .setField(ID_PASSWORD, "sweet") 2875 .setPresentation(createPresentation("The Dude")) 2876 .build()); 2877 mActivity.expectAutoFill("dude", "sweet"); 2878 2879 // Trigger auto-fill. 2880 requestFocusOnUsername(); 2881 sReplier.getNextFillRequest(); 2882 2883 // Auto-fill it. 2884 mUiBot.selectDataset("The Dude"); 2885 2886 // Check the results. 2887 mActivity.assertAutoFilled(); 2888 2889 // NOTE: need to call getSelectionEnd() inside the UI thread, otherwise it returns 0 2890 final AtomicInteger atomicBombToKillASmallInsect = new AtomicInteger(); 2891 2892 mActivity.onUsername((v) -> atomicBombToKillASmallInsect.set(v.getSelectionEnd())); 2893 assertWithMessage("Wrong position on username").that(atomicBombToKillASmallInsect.get()) 2894 .isEqualTo(4); 2895 2896 mActivity.onPassword((v) -> atomicBombToKillASmallInsect.set(v.getSelectionEnd())); 2897 assertWithMessage("Wrong position on password").that(atomicBombToKillASmallInsect.get()) 2898 .isEqualTo(5); 2899 } 2900 2901 @Test testAutofillLargeNumberOfDatasets()2902 public void testAutofillLargeNumberOfDatasets() throws Exception { 2903 // Set service. 2904 enableService(); 2905 2906 final StringBuilder bigStringBuilder = new StringBuilder(); 2907 for (int i = 0; i < 10_000; i++) { 2908 bigStringBuilder.append("BigAmI"); 2909 } 2910 final String bigString = bigStringBuilder.toString(); 2911 2912 final int size = 100; 2913 Log.d(TAG, "testAutofillLargeNumberOfDatasets(): " + size + " datasets with " 2914 + bigString.length() + "-bytes id"); 2915 2916 final CannedFillResponse.Builder response = new CannedFillResponse.Builder(); 2917 for (int i = 0; i < size; i++) { 2918 final String suffix = "-" + (i + 1); 2919 response.addDataset(new CannedDataset.Builder() 2920 .setField(ID_USERNAME, "user" + suffix) 2921 .setField(ID_PASSWORD, "pass" + suffix) 2922 .setId(bigString) 2923 .setPresentation(createPresentation("DS" + suffix)) 2924 .build()); 2925 } 2926 2927 // Set expectations. 2928 sReplier.addResponse(response.build()); 2929 2930 // Trigger auto-fill. 2931 requestFocusOnUsername(); 2932 sReplier.getNextFillRequest(); 2933 2934 // Make sure all datasets are shown. 2935 // TODO: improve assertDatasets() so it supports scrolling, and assert all of them are 2936 // shown. In fullscreen there are 4 items, otherwise there are 3 items. 2937 mUiBot.assertDatasetsContains("DS-1", "DS-2", "DS-3"); 2938 2939 // TODO: once it supports scrolling, selects the last dataset and asserts it's filled. 2940 } 2941 2942 @Presubmit 2943 @Test testCancellationSignalCalledAfterTimeout()2944 public void testCancellationSignalCalledAfterTimeout() throws Exception { 2945 // Set service. 2946 enableService(); 2947 2948 // Set expectations. 2949 final OneTimeCancellationSignalListener listener = 2950 new OneTimeCancellationSignalListener(Timeouts.FILL_TIMEOUT.ms() + 2000); 2951 sReplier.addResponse(DO_NOT_REPLY_RESPONSE); 2952 2953 // Trigger auto-fill. 2954 mActivity.onUsername(View::requestFocus); 2955 2956 // Attach listener to CancellationSignal. 2957 waitUntilConnected(); 2958 sReplier.getNextFillRequest().cancellationSignal.setOnCancelListener(listener); 2959 2960 // Assert results 2961 listener.assertOnCancelCalled(); 2962 } 2963 2964 @Test 2965 @AppModeFull(reason = "Unit test") testNewTextAttributes()2966 public void testNewTextAttributes() throws Exception { 2967 enableService(); 2968 sReplier.addResponse(NO_RESPONSE); 2969 mActivity.onUsername(View::requestFocus); 2970 2971 final FillRequest request = sReplier.getNextFillRequest(); 2972 final ViewNode username = findNodeByResourceId(request.structure, ID_USERNAME); 2973 assertThat(username.getMinTextEms()).isEqualTo(2); 2974 assertThat(username.getMaxTextEms()).isEqualTo(5); 2975 assertThat(username.getMaxTextLength()).isEqualTo(25); 2976 2977 final ViewNode container = findNodeByResourceId(request.structure, ID_USERNAME_CONTAINER); 2978 assertThat(container.getMinTextEms()).isEqualTo(-1); 2979 assertThat(container.getMaxTextEms()).isEqualTo(-1); 2980 assertThat(container.getMaxTextLength()).isEqualTo(-1); 2981 2982 final ViewNode password = findNodeByResourceId(request.structure, ID_PASSWORD); 2983 assertThat(password.getMinTextEms()).isEqualTo(-1); 2984 assertThat(password.getMaxTextEms()).isEqualTo(-1); 2985 assertThat(password.getMaxTextLength()).isEqualTo(5000); 2986 } 2987 2988 @Test testUiShowOnChangeAfterAutofill()2989 public void testUiShowOnChangeAfterAutofill() throws Exception { 2990 // Set service. 2991 enableService(); 2992 2993 // Set expectations. 2994 sReplier.addResponse(new CannedDataset.Builder() 2995 .setField(ID_USERNAME, "dude", createPresentation("dude")) 2996 .setField(ID_PASSWORD, "sweet", createPresentation("sweet")) 2997 .build()); 2998 mActivity.expectAutoFill("dude", "sweet"); 2999 3000 // Trigger auto-fill. 3001 requestFocusOnUsername(); 3002 mUiBot.assertDatasets("dude"); 3003 sReplier.getNextFillRequest(); 3004 mUiBot.selectDataset("dude"); 3005 3006 // Check the results. 3007 mActivity.assertAutoFilled(); 3008 mUiBot.assertNoDatasets(); 3009 3010 // Delete a character. 3011 sendKeyEvent("KEYCODE_DEL"); 3012 assertThat(mUiBot.getTextByRelativeId(ID_USERNAME)).isEqualTo("dud"); 3013 3014 mActivity.expectAutoFill("dude", "sweet"); 3015 3016 // Check autofill UI show. 3017 final UiObject2 datasetPicker = mUiBot.assertDatasets("dude"); 3018 3019 // Autofill again. 3020 mUiBot.selectDataset(datasetPicker, "dude"); 3021 3022 // Check the results. 3023 mActivity.assertAutoFilled(); 3024 mUiBot.assertNoDatasets(); 3025 } 3026 3027 @Test testUiShowOnChangeAfterAutofillOnePresentation()3028 public void testUiShowOnChangeAfterAutofillOnePresentation() throws Exception { 3029 // Set service. 3030 enableService(); 3031 3032 // Set expectations. 3033 sReplier.addResponse(new CannedDataset.Builder() 3034 .setField(ID_USERNAME, "dude") 3035 .setField(ID_PASSWORD, "sweet") 3036 .setPresentation(createPresentation("The Dude")) 3037 .build()); 3038 mActivity.expectAutoFill("dude", "sweet"); 3039 3040 // Trigger auto-fill. 3041 requestFocusOnUsername(); 3042 mUiBot.assertDatasets("The Dude"); 3043 sReplier.getNextFillRequest(); 3044 mUiBot.selectDataset("The Dude"); 3045 3046 // Check the results. 3047 mActivity.assertAutoFilled(); 3048 mUiBot.assertNoDatasets(); 3049 3050 // Delete username 3051 mUiBot.clearTextByRelativeId(ID_USERNAME); 3052 3053 mActivity.expectAutoFill("dude", "sweet"); 3054 3055 // Check autofill UI show. 3056 final UiObject2 datasetPicker = mUiBot.assertDatasets("The Dude"); 3057 3058 // Autofill again. 3059 mUiBot.selectDataset(datasetPicker, "The Dude"); 3060 3061 // Check the results. 3062 mActivity.assertAutoFilled(); 3063 mUiBot.assertNoDatasets(); 3064 } 3065 3066 @Presubmit 3067 @Test testCancelActionButton()3068 public void testCancelActionButton() throws Exception { 3069 // Set service. 3070 enableService(); 3071 3072 // Set expectations. 3073 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 3074 .addDataset(new CannedDataset.Builder() 3075 .setField(ID_USERNAME, "dude") 3076 .setField(ID_PASSWORD, "sweet") 3077 .setPresentation(createPresentationWithCancel("The Dude")) 3078 .build()) 3079 .setPresentationCancelIds(new int[]{R.id.cancel_fill}); 3080 sReplier.addResponse(builder.build()); 3081 3082 // Trigger auto-fill. 3083 mActivity.onUsername(View::requestFocus); 3084 sReplier.getNextFillRequest(); 3085 3086 mUiBot.assertDatasetsContains("The Dude"); 3087 3088 // Tap cancel button on fill UI 3089 mUiBot.selectByRelativeId(ID_CANCEL_FILL); 3090 mUiBot.waitForIdle(); 3091 3092 mUiBot.assertNoDatasets(); 3093 3094 // Test and verify auto-fill does not trigger 3095 mActivity.onPassword(View::requestFocus); 3096 mUiBot.waitForIdle(); 3097 3098 mUiBot.assertNoDatasetsEver(); 3099 3100 // Test and verify auto-fill does not trigger. 3101 mActivity.onUsername(View::requestFocus); 3102 mUiBot.waitForIdle(); 3103 3104 mUiBot.assertNoDatasetsEver(); 3105 3106 // Reset 3107 mActivity.tapClear(); 3108 3109 // Set expectations. 3110 final CannedFillResponse.Builder builder2 = new CannedFillResponse.Builder() 3111 .addDataset(new CannedDataset.Builder() 3112 .setField(ID_USERNAME, "dude") 3113 .setField(ID_PASSWORD, "sweet") 3114 .setPresentation(createPresentationWithCancel("The Dude")) 3115 .build()) 3116 .setPresentationCancelIds(new int[]{R.id.cancel}); 3117 sReplier.addResponse(builder2.build()); 3118 3119 // Trigger auto-fill. 3120 mActivity.onPassword(View::requestFocus); 3121 sReplier.getNextFillRequest(); 3122 3123 // Verify auto-fill has been triggered. 3124 mUiBot.assertDatasetsContains("The Dude"); 3125 } 3126 3127 @Presubmit 3128 @Test 3129 @AppModeFull(reason = "WRITE_SECURE_SETTING permission can't be grant to instant apps") testSwitchInputMethod_noNewFillRequest()3130 public void testSwitchInputMethod_noNewFillRequest() throws Exception { 3131 // TODO(b/187664861): Find better solution for small display device. 3132 mUiBot.assumeMinimumResolution(500); 3133 mUiBot.setScreenResolution(); 3134 // Set service 3135 enableService(); 3136 3137 // Set expectations 3138 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 3139 .addDataset(new CannedDataset.Builder() 3140 .setField(ID_USERNAME, "dude") 3141 .setField(ID_PASSWORD, "sweet") 3142 .setPresentation(createPresentation("The Dude")) 3143 .build()); 3144 sReplier.addResponse(builder.build()); 3145 3146 // Trigger auto-fill 3147 mActivity.onUsername(View::requestFocus); 3148 sReplier.getNextFillRequest(); 3149 3150 mUiBot.assertDatasetsContains("The Dude"); 3151 3152 // Trigger IME switch event 3153 Helper.mockSwitchInputMethod(sContext); 3154 mUiBot.waitForIdleSync(); 3155 3156 // Tap password field 3157 mUiBot.selectByRelativeId(ID_PASSWORD); 3158 mUiBot.waitForIdleSync(); 3159 3160 mUiBot.assertDatasetsContains("The Dude"); 3161 3162 // No new fill request 3163 sReplier.assertNoUnhandledFillRequests(); 3164 mUiBot.resetScreenResolution(); 3165 } 3166 } 3167