1 /* 2 * Copyright (C) 2020 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.inline; 18 19 import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE; 20 import static android.autofillservice.cts.testcore.Helper.ID_PASSWORD; 21 import static android.autofillservice.cts.testcore.Helper.ID_USERNAME; 22 import static android.autofillservice.cts.testcore.Helper.assertTextIsSanitized; 23 import static android.autofillservice.cts.testcore.Helper.disablePccDetectionFeature; 24 import static android.autofillservice.cts.testcore.Helper.enablePccDetectionFeature; 25 import static android.autofillservice.cts.testcore.Helper.findAutofillIdByResourceId; 26 import static android.autofillservice.cts.testcore.Helper.findNodeByResourceId; 27 import static android.autofillservice.cts.testcore.Helper.getContext; 28 import static android.autofillservice.cts.testcore.Helper.isPccFieldClassificationSet; 29 import static android.autofillservice.cts.testcore.InstrumentedAutoFillServiceInlineEnabled.SERVICE_CLASS; 30 import static android.autofillservice.cts.testcore.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME; 31 import static android.autofillservice.cts.testcore.Timeouts.MOCK_IME_TIMEOUT_MS; 32 import static android.view.View.AUTOFILL_HINT_USERNAME; 33 34 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent; 35 36 import static com.google.common.truth.Truth.assertThat; 37 import static com.google.common.truth.Truth.assertWithMessage; 38 39 import static org.junit.Assume.assumeTrue; 40 41 import android.accessibilityservice.AccessibilityServiceInfo; 42 import android.app.PendingIntent; 43 import android.app.UiAutomation; 44 import android.autofillservice.cts.activities.DummyActivity; 45 import android.autofillservice.cts.activities.NonAutofillableActivity; 46 import android.autofillservice.cts.activities.UsernameOnlyActivity; 47 import android.autofillservice.cts.commontests.LoginActivityCommonTestCase; 48 import android.autofillservice.cts.testcore.CannedFillResponse; 49 import android.autofillservice.cts.testcore.Helper; 50 import android.autofillservice.cts.testcore.IdMode; 51 import android.autofillservice.cts.testcore.InlineUiBot; 52 import android.autofillservice.cts.testcore.InstrumentedAutoFillService; 53 import android.content.Intent; 54 import android.os.Binder; 55 import android.os.Bundle; 56 import android.os.SystemClock; 57 import android.platform.test.annotations.AppModeFull; 58 import android.platform.test.annotations.Presubmit; 59 import android.service.autofill.FillContext; 60 import android.util.Log; 61 import android.view.accessibility.AccessibilityManager; 62 63 import androidx.test.platform.app.InstrumentationRegistry; 64 import androidx.test.uiautomator.Direction; 65 66 import com.android.cts.mockime.ImeEventStream; 67 import com.android.cts.mockime.MockImeSession; 68 69 import org.junit.After; 70 import org.junit.Ignore; 71 import org.junit.Test; 72 import org.junit.rules.TestRule; 73 74 import java.util.concurrent.CountDownLatch; 75 import java.util.concurrent.TimeUnit; 76 77 @Presubmit 78 public class InlineLoginActivityTest extends LoginActivityCommonTestCase { 79 80 private static final String TAG = "InlineLoginActivityTest"; 81 82 @Override enableService()83 protected void enableService() { 84 Helper.enableAutofillService(SERVICE_NAME); 85 InstrumentedAutoFillService.setAutofillServiceClass(SERVICE_CLASS); 86 } 87 InlineLoginActivityTest()88 public InlineLoginActivityTest() { 89 super(getInlineUiBot()); 90 } 91 92 @Override isInlineMode()93 protected boolean isInlineMode() { 94 return true; 95 } 96 97 @Override getMainTestRule()98 public TestRule getMainTestRule() { 99 return InlineUiBot.annotateRule(super.getMainTestRule()); 100 } 101 102 @After disablePcc()103 public void disablePcc() { 104 Log.d(TAG, "@After: disablePcc()"); 105 disablePccDetectionFeature(sContext); 106 } 107 108 @Test testAutofill_disjointDatasets()109 public void testAutofill_disjointDatasets() throws Exception { 110 // Set service. 111 enableService(); 112 113 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 114 .addDataset(new CannedFillResponse.CannedDataset.Builder() 115 .setField(ID_USERNAME, "dude") 116 .setPresentation(createPresentation("The Username")) 117 .setInlinePresentation(createInlinePresentation("The Username")) 118 .build()) 119 .addDataset(new CannedFillResponse.CannedDataset.Builder() 120 .setField(ID_PASSWORD, "sweet") 121 .setPresentation(createPresentation("The Password")) 122 .setInlinePresentation(createInlinePresentation("The Password")) 123 .build()) 124 .addDataset(new CannedFillResponse.CannedDataset.Builder() 125 .setField(ID_PASSWORD, "lollipop") 126 .setPresentation(createPresentation("The Password2")) 127 .setInlinePresentation(createInlinePresentation("The Password2")) 128 .build()); 129 130 sReplier.addResponse(builder.build()); 131 mActivity.expectAutoFill("dude"); 132 133 // Trigger auto-fill. 134 mUiBot.selectByRelativeId(ID_USERNAME); 135 mUiBot.waitForIdleSync(); 136 137 mUiBot.assertDatasets("The Username"); 138 139 // Switch focus to password 140 mUiBot.selectByRelativeId(ID_PASSWORD); 141 mUiBot.waitForIdleSync(); 142 143 mUiBot.assertDatasets("The Password", "The Password2"); 144 145 // Switch focus back to username 146 mUiBot.selectByRelativeId(ID_USERNAME); 147 mUiBot.waitForIdleSync(); 148 149 mUiBot.assertDatasets("The Username"); 150 mUiBot.selectDataset("The Username"); 151 mUiBot.waitForIdleSync(); 152 153 // Check the results. 154 mActivity.assertAutoFilled(); 155 156 // Make sure input was sanitized. 157 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 158 assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull(); 159 assertTextIsSanitized(request.structure, ID_PASSWORD); 160 final FillContext fillContext = request.contexts.get(request.contexts.size() - 1); 161 assertThat(fillContext.getFocusedId()) 162 .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME)); 163 164 // Make sure initial focus was properly set. 165 assertWithMessage("Username node is not focused").that( 166 findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue(); 167 assertWithMessage("Password node is focused").that( 168 findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse(); 169 } 170 171 @Test testAutofill_SwitchToAutofillableActivity()172 public void testAutofill_SwitchToAutofillableActivity() throws Exception { 173 assertAutofill_SwitchActivity(UsernameOnlyActivity.class, /* autofillable */ true); 174 } 175 176 @Test testAutofill_SwitchToNonAutofillableActivity()177 public void testAutofill_SwitchToNonAutofillableActivity() throws Exception { 178 assertAutofill_SwitchActivity(NonAutofillableActivity.class, /* autofillable */ false); 179 } 180 assertAutofill_SwitchActivity(Class<?> clazz, boolean autofillable)181 private void assertAutofill_SwitchActivity(Class<?> clazz, boolean autofillable) 182 throws Exception { 183 // Set service. 184 enableService(); 185 186 // Set expectations. 187 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 188 .addDataset(new CannedFillResponse.CannedDataset.Builder() 189 .setField(ID_USERNAME, "dude") 190 .setField(ID_PASSWORD, "password") 191 .setPresentation(createPresentation("The Username")) 192 .setInlinePresentation(createInlinePresentation("The Username")) 193 .build()); 194 sReplier.addResponse(builder.build()); 195 196 // Trigger auto-fill. 197 mUiBot.selectByRelativeId(ID_USERNAME); 198 mUiBot.waitForIdleSync(); 199 sReplier.getNextFillRequest(); 200 // Make sure the suggestion is shown. 201 mUiBot.assertDatasets("The Username"); 202 203 mUiBot.pressHome(); 204 mUiBot.waitForIdle(); 205 206 // Switch to another Activity 207 startActivity(clazz); 208 mUiBot.waitForIdle(); 209 210 // Trigger input method show. 211 mUiBot.selectByRelativeId(ID_USERNAME); 212 mUiBot.waitForIdleSync(); 213 if (autofillable) { 214 sReplier.addResponse(NO_RESPONSE); 215 sReplier.getNextFillRequest(); 216 } 217 // Make sure suggestion is not shown. 218 mUiBot.assertNoDatasets(); 219 } 220 startActivity(Class<?> clazz)221 protected final void startActivity(Class<?> clazz) { 222 final Intent intent = new Intent(mContext, clazz); 223 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 224 mContext.startActivity(intent); 225 } 226 227 @Test testAutofill_selectDatasetThenHideInlineSuggestion()228 public void testAutofill_selectDatasetThenHideInlineSuggestion() throws Exception { 229 // Set service. 230 enableService(); 231 232 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 233 .addDataset(new CannedFillResponse.CannedDataset.Builder() 234 .setField(ID_USERNAME, "dude") 235 .setPresentation(createPresentation("The Username")) 236 .setInlinePresentation(createInlinePresentation("The Username")) 237 .build()); 238 239 sReplier.addResponse(builder.build()); 240 mActivity.expectAutoFill("dude"); 241 242 // Trigger auto-fill. 243 mUiBot.selectByRelativeId(ID_USERNAME); 244 mUiBot.waitForIdleSync(); 245 246 mUiBot.assertDatasets("The Username"); 247 248 mUiBot.selectDataset("The Username"); 249 mUiBot.waitForIdleSync(); 250 251 mUiBot.assertNoDatasets(); 252 253 // Make sure input was sanitized. 254 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 255 assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull(); 256 assertTextIsSanitized(request.structure, ID_PASSWORD); 257 final FillContext fillContext = request.contexts.get(request.contexts.size() - 1); 258 assertThat(fillContext.getFocusedId()) 259 .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME)); 260 261 // Make sure initial focus was properly set. 262 assertWithMessage("Username node is not focused").that( 263 findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue(); 264 assertWithMessage("Password node is focused").that( 265 findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse(); 266 } 267 268 @Test testLongClickAttribution()269 public void testLongClickAttribution() throws Exception { 270 // Set service. 271 enableService(); 272 273 Intent intent = new Intent(mContext, DummyActivity.class); 274 PendingIntent pendingIntent = 275 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); 276 277 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 278 .addDataset(new CannedFillResponse.CannedDataset.Builder() 279 .setField(ID_USERNAME, "dude") 280 .setPresentation(createPresentation("The Username")) 281 .setInlinePresentation( 282 createInlinePresentation("The Username", pendingIntent)) 283 .build()); 284 285 sReplier.addResponse(builder.build()); 286 mActivity.expectAutoFill("dude"); 287 288 // Trigger auto-fill. 289 mUiBot.selectByRelativeId(ID_USERNAME); 290 mUiBot.waitForIdleSync(); 291 292 mUiBot.assertDatasets("The Username"); 293 294 // Long click on suggestion 295 mUiBot.longPressSuggestion("The Username"); 296 mUiBot.waitForIdleSync(); 297 298 // Make sure the attribution showed worked 299 mUiBot.selectByText("foo"); 300 301 // Go back to the filled app. 302 mUiBot.pressBack(); 303 304 sReplier.getNextFillRequest(); 305 mUiBot.waitForIdleSync(); 306 } 307 308 @Test 309 @AppModeFull(reason = "BROADCAST_STICKY permission cannot be granted to instant apps") testAutofill_noInvalid()310 public void testAutofill_noInvalid() throws Exception { 311 final String keyInvalid = "invalid"; 312 final String keyValid = "valid"; 313 final String message = "Passes valid message to the remote service"; 314 final Bundle bundle = new Bundle(); 315 bundle.putBinder(keyInvalid, new Binder()); 316 bundle.putString(keyValid, message); 317 318 // Set service. 319 enableService(); 320 final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession(); 321 assumeTrue("MockIME not available", mockImeSession != null); 322 323 mockImeSession.callSetInlineSuggestionsExtras(bundle); 324 325 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 326 .addDataset(new CannedFillResponse.CannedDataset.Builder() 327 .setField(ID_USERNAME, "dude") 328 .setPresentation(createPresentation("The Username")) 329 .setInlinePresentation(createInlinePresentation("The Username")) 330 .build()); 331 332 sReplier.addResponse(builder.build()); 333 334 // Trigger auto-fill. 335 mUiBot.selectByRelativeId(ID_USERNAME); 336 mUiBot.waitForIdleSync(); 337 338 mUiBot.assertDatasets("The Username"); 339 340 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 341 final Bundle extras = request.inlineRequest.getExtras(); 342 assertThat(extras.get(keyInvalid)).isNull(); 343 assertThat(extras.getString(keyValid)).isEqualTo(message); 344 345 final Bundle style = request.inlineRequest.getInlinePresentationSpecs().get(0).getStyle(); 346 assertThat(style.get(keyInvalid)).isNull(); 347 assertThat(style.getString(keyValid)).isEqualTo(message); 348 349 final Bundle style2 = request.inlineRequest.getInlinePresentationSpecs().get(1).getStyle(); 350 assertThat(style2.get(keyInvalid)).isNull(); 351 assertThat(style2.getString(keyValid)).isEqualTo(message); 352 } 353 354 @Test 355 @AppModeFull(reason = "WRITE_SECURE_SETTING permission can't be grant to instant apps") testSwitchInputMethod()356 public void testSwitchInputMethod() throws Exception { 357 // Set service 358 enableService(); 359 360 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 361 .addDataset(new CannedFillResponse.CannedDataset.Builder() 362 .setField(ID_USERNAME, "dude") 363 .setPresentation(createPresentation("The Username")) 364 .setInlinePresentation(createInlinePresentation("The Username")) 365 .build()); 366 367 sReplier.addResponse(builder.build()); 368 369 // Trigger auto-fill 370 mUiBot.selectByRelativeId(ID_USERNAME); 371 mUiBot.waitForIdleSync(); 372 373 mUiBot.assertDatasets("The Username"); 374 375 sReplier.getNextFillRequest(); 376 377 // Trigger IME switch event 378 Helper.mockSwitchInputMethod(sContext); 379 mUiBot.waitForIdleSync(); 380 381 final CannedFillResponse.Builder builder2 = new CannedFillResponse.Builder() 382 .addDataset(new CannedFillResponse.CannedDataset.Builder() 383 .setField(ID_USERNAME, "dude2") 384 .setPresentation(createPresentation("The Username 2")) 385 .setInlinePresentation(createInlinePresentation("The Username 2")) 386 .build()); 387 388 sReplier.addResponse(builder2.build()); 389 390 // Trigger auto-fill 391 mUiBot.selectByRelativeId(ID_USERNAME); 392 mUiBot.waitForIdleSync(); 393 394 // Confirm new suggestion 395 mUiBot.assertDatasets("The Username 2"); 396 397 // Confirm new fill request 398 sReplier.getNextFillRequest(); 399 } 400 401 @Test 402 @AppModeFull(reason = "BROADCAST_STICKY permission cannot be granted to instant apps") testImeDisableInlineSuggestions_fallbackDropdownUi()403 public void testImeDisableInlineSuggestions_fallbackDropdownUi() throws Exception { 404 // Set service. 405 enableService(); 406 407 final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession(); 408 assumeTrue("MockIME not available", mockImeSession != null); 409 410 // Disable inline suggestions for the default service. 411 final Bundle bundle = new Bundle(); 412 bundle.putBoolean("InlineSuggestions", false); 413 mockImeSession.callSetInlineSuggestionsExtras(bundle); 414 415 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 416 .addDataset(new CannedFillResponse.CannedDataset.Builder() 417 .setField(ID_USERNAME, "dude") 418 .setPresentation(createPresentation("The Username")) 419 .setInlinePresentation(createInlinePresentation("The Username")) 420 .build()); 421 sReplier.addResponse(builder.build()); 422 423 // Trigger auto-fill. 424 mUiBot.selectByRelativeId(ID_USERNAME); 425 mUiBot.waitForIdleSync(); 426 427 // Check that no inline requests are sent to the service. 428 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 429 assertThat(request.inlineRequest).isNull(); 430 431 // Check dropdown UI shown. 432 getDropdownUiBot().assertDatasets("The Username"); 433 } 434 435 @Test testTouchExplorationEnabledImeSupportInline_inlineShown()436 public void testTouchExplorationEnabledImeSupportInline_inlineShown() throws Exception { 437 enableTouchExploration(); 438 439 enableService(); 440 441 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 442 .addDataset(new CannedFillResponse.CannedDataset.Builder() 443 .setField(ID_USERNAME, "dude") 444 .setPresentation(createPresentation("The Username")) 445 .setInlinePresentation(createInlinePresentation("The Username")) 446 .build()); 447 sReplier.addResponse(builder.build()); 448 449 try { 450 // Trigger auto-fill. 451 mUiBot.selectByRelativeId(ID_USERNAME); 452 mUiBot.waitForIdleSync(); 453 454 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 455 assertThat(request.inlineRequest).isNotNull(); 456 457 // Check datasets shown. 458 mUiBot.assertDatasets("The Username"); 459 } catch (Exception e) { 460 throw e; 461 } finally { 462 resetTouchExploration(); 463 } 464 } 465 466 @Test testScrollSuggestionView()467 public void testScrollSuggestionView() throws Exception { 468 // Set service. 469 enableService(); 470 471 final int firstDataset = 1; 472 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder(); 473 for (int i = firstDataset; i <= 20; i++) { 474 builder.addDataset(new CannedFillResponse.CannedDataset.Builder() 475 .setField(ID_USERNAME, "dude" + i) 476 .setPresentation(createPresentation("Username" + i)) 477 .setInlinePresentation(createInlinePresentation("Username" + i)) 478 .build()); 479 } 480 481 sReplier.addResponse(builder.build()); 482 483 // Trigger auto-fill. 484 mUiBot.selectByRelativeId(ID_USERNAME); 485 mUiBot.waitForIdleSync(); 486 487 mUiBot.assertSuggestion("Username" + firstDataset); 488 489 // Scroll the suggestion view 490 mUiBot.scrollSuggestionView(Direction.RIGHT, /* speed */ 3000); 491 mUiBot.waitForIdleSync(); 492 493 mUiBot.assertNoSuggestion("Username" + firstDataset); 494 495 sReplier.getNextFillRequest(); 496 mUiBot.waitForIdleSync(); 497 } 498 499 @Test testClickEventPassToIme()500 public void testClickEventPassToIme() throws Exception { 501 testTouchEventPassToIme(/* longPress */ false); 502 } 503 504 @Test testLongClickEventPassToIme()505 public void testLongClickEventPassToIme() throws Exception { 506 testTouchEventPassToIme(/* longPress */ true); 507 } 508 testTouchEventPassToIme(boolean longPress)509 private void testTouchEventPassToIme(boolean longPress) throws Exception { 510 final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession(); 511 assumeTrue("MockIME not available", mockImeSession != null); 512 513 // Set service. 514 enableService(); 515 516 Intent intent = new Intent(mContext, DummyActivity.class); 517 PendingIntent pendingIntent = 518 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); 519 520 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 521 .addDataset(new CannedFillResponse.CannedDataset.Builder() 522 .setField(ID_USERNAME, "dude") 523 .setPresentation(createPresentation("The Username")) 524 .setInlinePresentation(longPress 525 ? createInlinePresentation("The Username", pendingIntent) 526 : createInlinePresentation("The Username")) 527 .build()); 528 529 sReplier.addResponse(builder.build()); 530 531 final ImeEventStream stream = mockImeSession.openEventStream(); 532 533 // Trigger auto-fill. 534 mUiBot.selectByRelativeId(ID_USERNAME); 535 mUiBot.waitForIdleSync(); 536 sReplier.getNextFillRequest(); 537 538 mUiBot.assertDatasets("The Username"); 539 540 if (longPress) { 541 // Long click on suggestion 542 mUiBot.longPressSuggestion("The Username"); 543 544 expectEvent(stream, 545 event -> "onInlineSuggestionLongClickedEvent".equals(event.getEventName()), 546 MOCK_IME_TIMEOUT_MS); 547 } else { 548 // Click on suggestion 549 mUiBot.selectDataset("The Username"); 550 551 expectEvent(stream, 552 event -> "onInlineSuggestionClickedEvent".equals(event.getEventName()), 553 MOCK_IME_TIMEOUT_MS); 554 } 555 } 556 557 @Test testInlineSuggestionViewReleased()558 public void testInlineSuggestionViewReleased() throws Exception { 559 // Set service 560 enableService(); 561 562 // Prepare the autofill response 563 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 564 .addDataset(new CannedFillResponse.CannedDataset.Builder() 565 .setField(ID_USERNAME, "dude") 566 .setPresentation(createPresentation("The Username")) 567 .setInlinePresentation(createInlinePresentation("The Username")) 568 .build()) 569 .addDataset(new CannedFillResponse.CannedDataset.Builder() 570 .setField(ID_PASSWORD, "sweet") 571 .setPresentation(createPresentation("The Password")) 572 .setInlinePresentation(createInlinePresentation("The Password")) 573 .build()) 574 .addDataset(new CannedFillResponse.CannedDataset.Builder() 575 .setField(ID_PASSWORD, "lollipop") 576 .setPresentation(createPresentation("The Password2")) 577 .setInlinePresentation(createInlinePresentation("The Password2")) 578 .build()); 579 sReplier.addResponse(builder.build()); 580 581 // Trigger auto-fill on username field 582 mUiBot.selectByRelativeId(ID_USERNAME); 583 mUiBot.waitForIdleSync(); 584 mUiBot.assertDatasets("The Username"); 585 Helper.assertActiveViewCountFromInlineSuggestionRenderService(1); 586 587 // Switch focus to password 588 mUiBot.selectByRelativeId(ID_PASSWORD); 589 mUiBot.waitForIdleSync(); 590 mUiBot.assertDatasets("The Password", "The Password2"); 591 Helper.assertActiveViewCountFromInlineSuggestionRenderService(2); 592 593 // Switch focus back to username 594 mUiBot.selectByRelativeId(ID_USERNAME); 595 mUiBot.waitForIdleSync(); 596 mUiBot.assertDatasets("The Username"); 597 Helper.assertActiveViewCountFromInlineSuggestionRenderService(1); 598 599 // Select the autofill suggestion on username, then check the results 600 mActivity.expectAutoFill("dude"); 601 mUiBot.selectDataset("The Username"); 602 mUiBot.waitForIdleSync(); 603 mActivity.assertAutoFilled(); 604 sReplier.getNextFillRequest(); 605 606 // Sleep for a while for the wait in {@link com.android.server.autofill.ui 607 // .RemoteInlineSuggestionUi} to timeout. 608 SystemClock.sleep(500); 609 Helper.assertActiveViewCountFromInlineSuggestionRenderService(0); 610 } 611 612 @Test 613 @Ignore("b/281726966") testAutofill_pccDatasets()614 public void testAutofill_pccDatasets() throws Exception { 615 // Set service. 616 enableService(); 617 enablePccDetectionFeature(sContext, "username"); 618 sReplier.setIdMode(IdMode.PCC_ID); 619 620 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 621 .addDataset(new CannedFillResponse.CannedDataset.Builder() 622 .setField(ID_USERNAME, "dude") 623 .setField(ID_PASSWORD, "sweet") 624 .setPresentation(createPresentation("The Dude")) 625 .build()) 626 .addDataset(new CannedFillResponse.CannedDataset.Builder() 627 .setField(ID_USERNAME, "user1") 628 .setField(ID_PASSWORD, "pass1") 629 .setPresentation(createPresentation("generic user")) 630 .build()); 631 sReplier.addResponse(builder.build()); 632 633 // Trigger auto-fill. 634 mUiBot.selectByRelativeId(ID_USERNAME); 635 mUiBot.waitForIdleSync(); 636 637 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 638 if (isPccFieldClassificationSet(sContext)) { 639 assertThat(request.hints.size()).isEqualTo(1); 640 assertThat(request.hints.get(0)).isEqualTo("username"); 641 } 642 disablePccDetectionFeature(sContext); 643 sReplier.setIdMode(IdMode.RESOURCE_ID); 644 } 645 646 @Test 647 @Ignore("b/281726966") autofillPccDatasetTest_setForAllHints()648 public void autofillPccDatasetTest_setForAllHints() throws Exception { 649 // Set service. 650 enableService(); 651 enablePccDetectionFeature(sContext, "username", "password", "new_password"); 652 sReplier.setIdMode(IdMode.PCC_ID); 653 654 final CannedFillResponse.Builder builder = new CannedFillResponse.Builder() 655 .addDataset(new CannedFillResponse.CannedDataset.Builder() 656 .setField(AUTOFILL_HINT_USERNAME, "dude") 657 .setField("allField1") 658 .setPresentation(createPresentation("The Dude")) 659 .build()) 660 .addDataset(new CannedFillResponse.CannedDataset.Builder() 661 .setField("allField2") 662 .setPresentation(createPresentation("generic user")) 663 .build()); 664 sReplier.addResponse(builder.build()); 665 666 // Trigger auto-fill. 667 mUiBot.selectByRelativeId(ID_USERNAME); 668 mUiBot.waitForIdleSync(); 669 670 final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest(); 671 if (isPccFieldClassificationSet(sContext)) { 672 assertThat(request.hints.size()).isEqualTo(3); 673 } 674 675 disablePccDetectionFeature(sContext); 676 sReplier.setIdMode(IdMode.RESOURCE_ID); 677 } 678 enableTouchExploration()679 private void enableTouchExploration() throws InterruptedException { 680 toggleTouchExploration(/*enable=*/ true); 681 } 682 resetTouchExploration()683 private void resetTouchExploration() throws InterruptedException { 684 toggleTouchExploration(/*enable=*/ false); 685 } 686 toggleTouchExploration(boolean enable)687 private void toggleTouchExploration(boolean enable) 688 throws InterruptedException { 689 final AccessibilityManager manager = 690 getContext().getSystemService(AccessibilityManager.class); 691 if (isTouchExplorationEnabled(manager) == enable) { 692 return; 693 } 694 695 final CountDownLatch latch = new CountDownLatch(1); 696 AccessibilityManager.TouchExplorationStateChangeListener serviceListener = 697 (boolean newState) -> { 698 if (newState == enable) { 699 latch.countDown(); 700 } 701 }; 702 manager.addTouchExplorationStateChangeListener(serviceListener); 703 704 final UiAutomation uiAutomation = 705 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 706 final AccessibilityServiceInfo info = uiAutomation.getServiceInfo(); 707 assert info != null; 708 if (enable) { 709 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; 710 } else { 711 info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; 712 } 713 uiAutomation.setServiceInfo(info); 714 715 // Wait for touch exploration state to be toggled 716 assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); 717 718 if (enable) { 719 assertThat(isTouchExplorationEnabled(manager)).isTrue(); 720 } else { 721 assertThat(isTouchExplorationEnabled(manager)).isFalse(); 722 } 723 manager.removeTouchExplorationStateChangeListener(serviceListener); 724 } 725 isTouchExplorationEnabled(AccessibilityManager manager)726 private static boolean isTouchExplorationEnabled(AccessibilityManager manager) { 727 return manager.isEnabled() && manager.isTouchExplorationEnabled(); 728 } 729 } 730