1 /* 2 * Copyright (C) 2018 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.contentcaptureservice.cts; 18 19 import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext; 20 import static android.contentcaptureservice.cts.Assertions.assertContextUpdated; 21 import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared; 22 import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext; 23 import static android.contentcaptureservice.cts.Assertions.assertRightActivity; 24 import static android.contentcaptureservice.cts.Assertions.assertRightRelationship; 25 import static android.contentcaptureservice.cts.Assertions.assertSessionId; 26 import static android.contentcaptureservice.cts.Assertions.assertSessionPaused; 27 import static android.contentcaptureservice.cts.Assertions.assertSessionResumed; 28 import static android.contentcaptureservice.cts.Assertions.assertViewAppeared; 29 import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished; 30 import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted; 31 import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared; 32 import static android.contentcaptureservice.cts.Assertions.assertWindowBoundsChanged; 33 import static android.contentcaptureservice.cts.Helper.MY_PACKAGE; 34 import static android.contentcaptureservice.cts.Helper.newImportantView; 35 import static android.view.contentcapture.DataRemovalRequest.FLAG_IS_PREFIX; 36 37 import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED; 38 import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED; 39 40 import static com.google.common.truth.Truth.assertThat; 41 import static com.google.common.truth.Truth.assertWithMessage; 42 43 import android.content.ComponentName; 44 import android.content.Context; 45 import android.content.ContextParams; 46 import android.content.LocusId; 47 import android.contentcaptureservice.cts.CtsContentCaptureService.Session; 48 import android.os.Bundle; 49 import android.os.SystemClock; 50 import android.platform.test.annotations.AppModeFull; 51 import android.text.Editable; 52 import android.util.ArraySet; 53 import android.util.Log; 54 import android.view.View; 55 import android.view.WindowManager; 56 import android.view.autofill.AutofillId; 57 import android.view.contentcapture.ContentCaptureContext; 58 import android.view.contentcapture.ContentCaptureEvent; 59 import android.view.contentcapture.ContentCaptureSession; 60 import android.view.contentcapture.ContentCaptureSessionId; 61 import android.view.contentcapture.DataRemovalRequest; 62 import android.view.contentcapture.DataRemovalRequest.LocusIdRequest; 63 import android.view.inputmethod.BaseInputConnection; 64 import android.view.inputmethod.EditorInfo; 65 import android.view.inputmethod.InputConnection; 66 import android.widget.EditText; 67 import android.widget.LinearLayout; 68 import android.widget.TextView; 69 70 import androidx.annotation.NonNull; 71 import androidx.test.rule.ActivityTestRule; 72 73 import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher; 74 import com.android.compatibility.common.util.DoubleVisitor; 75 76 import org.junit.After; 77 import org.junit.Before; 78 import org.junit.Test; 79 80 import java.util.List; 81 import java.util.Set; 82 import java.util.concurrent.atomic.AtomicReference; 83 84 @AppModeFull(reason = "BlankWithTitleActivityTest is enough") 85 public class LoginActivityTest 86 extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<LoginActivity> { 87 88 private static final String TAG = LoginActivityTest.class.getSimpleName(); 89 90 private static final int NO_FLAGS = 0; 91 92 private static final ActivityTestRule<LoginActivity> sActivityRule = new ActivityTestRule<>( 93 LoginActivity.class, false, false); 94 LoginActivityTest()95 public LoginActivityTest() { 96 super(LoginActivity.class); 97 } 98 99 @Override getActivityTestRule()100 protected ActivityTestRule<LoginActivity> getActivityTestRule() { 101 return sActivityRule; 102 } 103 104 @Before 105 @After resetActivityStaticState()106 public void resetActivityStaticState() { 107 LoginActivity.onRootView(null); 108 } 109 110 @Test testSimpleLifecycle_defaultSession()111 public void testSimpleLifecycle_defaultSession() throws Exception { 112 final CtsContentCaptureService service = enableService(); 113 final ActivityWatcher watcher = startWatcher(); 114 115 final LoginActivity activity = launchActivity(); 116 watcher.waitFor(RESUMED); 117 final int taskId = activity.getTaskId(); 118 119 activity.finish(); 120 watcher.waitFor(DESTROYED); 121 122 final Session session = service.getOnlyFinishedSession(); 123 Log.v(TAG, "session id: " + session.id); 124 125 activity.assertDefaultEvents(session); 126 127 final ComponentName name = activity.getComponentName(); 128 service.assertThat() 129 .activityResumed(name, taskId) 130 .activityPaused(name, taskId); 131 } 132 133 @Test testContentCaptureSessionCache()134 public void testContentCaptureSessionCache() throws Exception { 135 final CtsContentCaptureService service = enableService(); 136 final ActivityWatcher watcher = startWatcher(); 137 138 final ContentCaptureContext clientContext = newContentCaptureContext(); 139 140 final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>(); 141 final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>(); 142 143 LoginActivity.onRootView((activity, rootView) -> { 144 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 145 mainSessionRef.set(mainSession); 146 final ContentCaptureSession childSession = mainSession 147 .createContentCaptureSession(clientContext); 148 childSessionRef.set(childSession); 149 150 rootView.setContentCaptureSession(childSession); 151 // Already called getContentCaptureSession() earlier, use cached session (main). 152 assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession); 153 154 rootView.setContentCaptureSession(mainSession); 155 assertThat(rootView.getContentCaptureSession()).isEqualTo(mainSession); 156 157 rootView.setContentCaptureSession(childSession); 158 assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession); 159 }); 160 161 final LoginActivity activity = launchActivity(); 162 watcher.waitFor(RESUMED); 163 164 activity.finish(); 165 watcher.waitFor(DESTROYED); 166 167 final ContentCaptureSessionId childSessionId = childSessionRef.get() 168 .getContentCaptureSessionId(); 169 170 assertSessionId(childSessionId, activity.getRootView()); 171 } 172 173 @Test testSimpleLifecycle_rootViewSession()174 public void testSimpleLifecycle_rootViewSession() throws Exception { 175 final CtsContentCaptureService service = enableService(); 176 final ActivityWatcher watcher = startWatcher(); 177 178 final ContentCaptureContext clientContext = newContentCaptureContext(); 179 180 final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>(); 181 final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>(); 182 183 LoginActivity.onRootView((activity, rootView) -> { 184 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 185 mainSessionRef.set(mainSession); 186 final ContentCaptureSession childSession = mainSession 187 .createContentCaptureSession(clientContext); 188 childSessionRef.set(childSession); 189 Log.i(TAG, "Setting root view (" + rootView + ") session to " + childSession); 190 rootView.setContentCaptureSession(childSession); 191 }); 192 193 final LoginActivity activity = launchActivity(); 194 watcher.waitFor(RESUMED); 195 196 activity.finish(); 197 watcher.waitFor(DESTROYED); 198 199 final ContentCaptureSessionId mainSessionId = mainSessionRef.get() 200 .getContentCaptureSessionId(); 201 final ContentCaptureSessionId childSessionId = childSessionRef.get() 202 .getContentCaptureSessionId(); 203 Log.v(TAG, "session ids: main=" + mainSessionId + ", child=" + childSessionId); 204 205 // Initial checks 206 assertSessionId(childSessionId, activity.getRootView()); 207 assertSessionId(childSessionId, activity.mUsernameLabel); 208 assertSessionId(childSessionId, activity.mUsername); 209 assertSessionId(childSessionId, activity.mPassword); 210 assertSessionId(childSessionId, activity.mPasswordLabel); 211 212 // Get the sessions 213 final Session mainSession = service.getFinishedSession(mainSessionId); 214 final Session childSession = service.getFinishedSession(childSessionId); 215 216 assertRightActivity(mainSession, mainSessionId, activity); 217 assertRightRelationship(mainSession, childSession); 218 219 // Initial check 220 final List<ContentCaptureSessionId> allSessionIds = service.getAllSessionIds(); 221 assertThat(allSessionIds).containsExactly(mainSessionId, childSessionId); 222 223 /* 224 * Asserts main session 225 */ 226 227 // Checks context 228 assertMainSessionContext(mainSession, activity); 229 230 // Check events 231 final List<ContentCaptureEvent> unfilteredEvents = mainSession.getUnfilteredEvents(); 232 assertWindowBoundsChanged(unfilteredEvents); 233 234 final List<ContentCaptureEvent> mainEvents = mainSession.getEvents(); 235 Log.v(TAG, "events(" + mainEvents.size() + ") for main session: " + mainEvents); 236 237 final View grandpa1 = activity.getGrandParent(); 238 final View grandpa2 = activity.getGrandGrandParent(); 239 final View decorView = activity.getDecorView(); 240 final AutofillId rootId = activity.getRootView().getAutofillId(); 241 242 final int minEvents = 7; // TODO(b/122315042): disappeared not always sent 243 assertThat(mainEvents.size()).isAtLeast(minEvents); 244 assertSessionResumed(mainEvents, 0); 245 assertViewTreeStarted(mainEvents, 1); 246 assertDecorViewAppeared(mainEvents, 2, decorView); 247 assertViewAppeared(mainEvents, 3, grandpa2, decorView.getAutofillId()); 248 assertViewAppeared(mainEvents, 4, grandpa1, grandpa2.getAutofillId()); 249 assertViewTreeFinished(mainEvents, 5); 250 // TODO(b/122315042): these assertions are currently a mess, so let's disable for now and 251 // properly fix them later... 252 if (false) { 253 int pausedIndex = 6; 254 final boolean disappeared = assertViewsOptionallyDisappeared(mainEvents, pausedIndex, 255 decorView.getAutofillId(), 256 grandpa2.getAutofillId(), grandpa1.getAutofillId()); 257 if (disappeared) { 258 pausedIndex += 3; 259 } 260 assertSessionPaused(mainEvents, pausedIndex); 261 } 262 263 /* 264 * Asserts child session 265 */ 266 267 // Checks context 268 assertChildSessionContext(childSession, "file://dev/null"); 269 270 assertContentCaptureContext(childSession.context); 271 272 // Check events 273 final List<ContentCaptureEvent> childEvents = childSession.getEvents(); 274 Log.v(TAG, "events for child session: " + childEvents); 275 final int minChildEvents = 5; 276 assertThat(childEvents.size()).isAtLeast(minChildEvents); 277 assertViewAppeared(childEvents, 0, childSessionId, activity.getRootView(), 278 grandpa1.getAutofillId()); 279 assertViewAppeared(childEvents, 1, childSessionId, activity.mUsernameLabel, rootId); 280 assertViewAppeared(childEvents, 2, childSessionId, activity.mUsername, rootId); 281 assertViewAppeared(childEvents, 3, childSessionId, activity.mPasswordLabel, rootId); 282 assertViewAppeared(childEvents, 4, childSessionId, activity.mPassword, rootId); 283 284 assertViewsOptionallyDisappeared(childEvents, minChildEvents, 285 rootId, 286 activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(), 287 activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId()); 288 } 289 290 @Test testSimpleLifecycle_changeContextAfterCreate()291 public void testSimpleLifecycle_changeContextAfterCreate() throws Exception { 292 final CtsContentCaptureService service = enableService(); 293 final ActivityWatcher watcher = startWatcher(); 294 295 final LoginActivity activity = launchActivity(); 296 watcher.waitFor(RESUMED); 297 298 final ContentCaptureContext newContext1 = newContentCaptureContext(); 299 final ContentCaptureContext newContext2 = null; 300 301 final View rootView = activity.getRootView(); 302 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 303 assertThat(mainSession).isNotNull(); 304 Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext1); 305 mainSession.setContentCaptureContext(newContext1); 306 assertContentCaptureContext(mainSession.getContentCaptureContext()); 307 308 Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext2); 309 mainSession.setContentCaptureContext(newContext2); 310 311 activity.finish(); 312 watcher.waitFor(DESTROYED); 313 314 final Session session = service.getOnlyFinishedSession(); 315 Log.v(TAG, "session id: " + session.id); 316 317 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 318 319 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 2) 320 .assertContextUpdated(); 321 322 final ContentCaptureEvent event1 = assertor.getLastEvent(); 323 final ContentCaptureContext actualContext = event1.getContentCaptureContext(); 324 assertContentCaptureContext(actualContext); 325 326 assertor.assertContextUpdated(); 327 328 final ContentCaptureEvent event2 = assertor.getLastEvent(); 329 assertThat(event2.getContentCaptureContext()).isNull(); 330 } 331 332 @Test testSimpleLifecycle_changeContextOnCreate()333 public void testSimpleLifecycle_changeContextOnCreate() throws Exception { 334 final CtsContentCaptureService service = enableService(); 335 final ActivityWatcher watcher = startWatcher(); 336 337 final ContentCaptureContext newContext = newContentCaptureContext(); 338 339 LoginActivity.onRootView((activity, rootView) -> { 340 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 341 Log.i(TAG, "Setting root view (" + rootView + ") context to " + newContext); 342 mainSession.setContentCaptureContext(newContext); 343 assertContentCaptureContext(mainSession.getContentCaptureContext()); 344 }); 345 346 final LoginActivity activity = launchActivity(); 347 watcher.waitFor(RESUMED); 348 349 activity.finish(); 350 watcher.waitFor(DESTROYED); 351 352 final Session session = service.getOnlyFinishedSession(); 353 Log.v(TAG, "session id: " + session.id); 354 final ContentCaptureSessionId sessionId = session.id; 355 assertRightActivity(session, sessionId, activity); 356 357 // Initial check 358 359 final List<ContentCaptureEvent> events = session.getEvents(); 360 Log.v(TAG, "events(" + events.size() + "): " + events); 361 // TODO(b/123540067): ideally it should be X so it reflects just the views defined 362 // in the layout - right now it's generating events for 2 intermediate parents 363 // (android:action_mode_bar_stub and android:content), we should try to create an 364 // activity without them 365 366 final AutofillId rootId = activity.getRootView().getAutofillId(); 367 368 assertThat(events.size()).isAtLeast(11); 369 370 // TODO(b/123540067): get rid of those intermediated parents 371 final View grandpa1 = activity.getGrandParent(); 372 final View grandpa2 = activity.getGrandGrandParent(); 373 final View decorView = activity.getDecorView(); 374 final View rootView = activity.getRootView(); 375 376 final ContentCaptureEvent ctxUpdatedEvent = assertContextUpdated(events, 0); 377 final ContentCaptureContext actualContext = ctxUpdatedEvent.getContentCaptureContext(); 378 assertContentCaptureContext(actualContext); 379 380 assertSessionResumed(events, 1); 381 assertViewTreeStarted(events, 2); 382 assertDecorViewAppeared(events, 3, decorView); 383 assertViewAppeared(events, 4, grandpa2, decorView.getAutofillId()); 384 assertViewAppeared(events, 5, grandpa1, grandpa2.getAutofillId()); 385 assertViewAppeared(events, 6, sessionId, rootView, grandpa1.getAutofillId()); 386 assertViewAppeared(events, 7, sessionId, activity.mUsernameLabel, rootId); 387 assertViewAppeared(events, 8, sessionId, activity.mUsername, rootId); 388 assertViewAppeared(events, 9, sessionId, activity.mPasswordLabel, rootId); 389 assertViewAppeared(events, 10, sessionId, activity.mPassword, rootId); 390 } 391 392 @Test testTextChanged()393 public void testTextChanged() throws Exception { 394 final CtsContentCaptureService service = enableService(); 395 final ActivityWatcher watcher = startWatcher(); 396 397 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 398 .setText("user")); 399 400 final LoginActivity activity = launchActivity(); 401 watcher.waitFor(RESUMED); 402 403 activity.syncRunOnUiThread(() -> { 404 activity.mUsername.setText("USER"); 405 activity.mPassword.setText("PASS"); 406 }); 407 // wait to sent event 408 sleep(); 409 410 activity.finish(); 411 watcher.waitFor(DESTROYED); 412 413 final Session session = service.getOnlyFinishedSession(); 414 415 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 416 417 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 2) 418 .assertViewTextChanged(activity.mUsername.getAutofillId(), "USER") 419 .assertViewTextChanged(activity.mPassword.getAutofillId(), "PASS"); 420 421 activity.assertInitialViewsDisappeared(assertor); 422 } 423 424 @Test testTextChangeBuffer()425 public void testTextChangeBuffer() throws Exception { 426 final CtsContentCaptureService service = enableService(); 427 final ActivityWatcher watcher = startWatcher(); 428 429 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 430 .setText("")); 431 432 final LoginActivity activity = launchActivity(); 433 watcher.waitFor(RESUMED); 434 435 activity.syncRunOnUiThread(() -> { 436 activity.mUsername.setText("a"); 437 activity.mUsername.setText("ab"); 438 activity.mUsername.setText(""); 439 activity.mUsername.setText("abc"); 440 441 activity.mPassword.setText("d"); 442 activity.mPassword.setText(""); 443 activity.mPassword.setText(""); 444 activity.mPassword.setText("de"); 445 activity.mPassword.setText("def"); 446 activity.mPassword.setText(""); 447 448 activity.mUsername.setText("abc"); 449 }); 450 // wait to sent event 451 sleep(); 452 453 activity.finish(); 454 watcher.waitFor(DESTROYED); 455 456 final Session session = service.getOnlyFinishedSession(); 457 458 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 459 460 final AutofillId usernameId = activity.mUsername.getAutofillId(); 461 final AutofillId passwordId = activity.mPassword.getAutofillId(); 462 463 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 8) 464 .assertViewTextChanged(activity.mUsername.getAutofillId(), "a") 465 .assertViewTextChanged(usernameId, "ab") 466 .assertViewTextChanged(usernameId, "") 467 .assertViewTextChanged(usernameId, "abc") 468 .assertViewTextChanged(passwordId, "d") 469 .assertViewTextChanged(passwordId, "") 470 .assertViewTextChanged(passwordId, "") 471 .assertViewTextChanged(passwordId, "de") 472 .assertViewTextChanged(passwordId, "def") 473 .assertViewTextChanged(passwordId, "") 474 .assertViewTextChanged(usernameId, "abc"); 475 476 activity.assertInitialViewsDisappeared(assertor); 477 } 478 479 @Test testComposingSpan_mergedEvent()480 public void testComposingSpan_mergedEvent() throws Exception { 481 final CtsContentCaptureService service = enableService(); 482 final ActivityWatcher watcher = startWatcher(); 483 484 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 485 .setText("")); 486 487 final LoginActivity activity = launchActivity(); 488 watcher.waitFor(RESUMED); 489 490 activity.syncRunOnUiThread(() -> { 491 // add text with composing span. 492 appendText(activity.mUsername, "A"); 493 appendText(activity.mUsername, "n"); 494 appendText(activity.mUsername, "d"); 495 appendText(activity.mUsername, "r"); 496 appendText(activity.mUsername, "o"); 497 appendText(activity.mUsername, "i"); 498 appendText(activity.mUsername, "d"); 499 }); 500 // wait to sent event 501 sleep(); 502 503 activity.finish(); 504 watcher.waitFor(DESTROYED); 505 506 final Session session = service.getOnlyFinishedSession(); 507 508 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 509 510 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 5) 511 .assertViewTextChanged(activity.mUsername.getAutofillId(), "Android"); 512 513 activity.assertInitialViewsDisappeared(assertor); 514 } 515 516 @Test testComposingSpan_notMergedWithoutComposing()517 public void testComposingSpan_notMergedWithoutComposing() throws Exception { 518 final CtsContentCaptureService service = enableService(); 519 final ActivityWatcher watcher = startWatcher(); 520 521 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 522 .setText("")); 523 524 final LoginActivity activity = launchActivity(); 525 watcher.waitFor(RESUMED); 526 527 activity.syncRunOnUiThread(() -> { 528 // add text with composing span. 529 appendText(activity.mUsername, "G"); 530 appendText(activity.mUsername, "o"); 531 appendText(activity.mUsername, "o"); 532 appendText(activity.mUsername, "d"); 533 534 // append text without composing span 535 appendText(activity.mUsername, " ", false); 536 537 // append text with composing span, again. 538 appendText(activity.mUsername, "m"); 539 appendText(activity.mUsername, "orning"); 540 }); 541 // wait to sent event 542 sleep(); 543 544 activity.finish(); 545 watcher.waitFor(DESTROYED); 546 547 final Session session = service.getOnlyFinishedSession(); 548 549 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 550 551 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 4) 552 .assertViewTextChanged(activity.mUsername.getAutofillId(), "Good"); 553 554 assertor.assertViewTextChanged(activity.mUsername.getAutofillId(), "Good "); 555 556 assertor.assertViewTextChanged(activity.mUsername.getAutofillId(), "Good morning"); 557 558 activity.assertInitialViewsDisappeared(assertor); 559 } 560 561 @Test testComposingSpan_differentEditText()562 public void testComposingSpan_differentEditText() throws Exception { 563 final CtsContentCaptureService service = enableService(); 564 final ActivityWatcher watcher = startWatcher(); 565 566 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 567 .setText("")); 568 569 final LoginActivity activity = launchActivity(); 570 watcher.waitFor(RESUMED); 571 572 activity.syncRunOnUiThread(() -> { 573 // add text with composing span. 574 appendText(activity.mUsername, "Good"); 575 // add text with composing span on the different EditText. 576 appendText(activity.mPassword, "How"); 577 // switch again. 578 appendText(activity.mUsername, " morning"); 579 appendText(activity.mPassword, " are you"); 580 }); 581 // wait to sent event 582 sleep(); 583 584 activity.finish(); 585 watcher.waitFor(DESTROYED); 586 587 final Session session = service.getOnlyFinishedSession(); 588 589 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 590 591 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 3) 592 .assertViewTextChanged(activity.mUsername.getAutofillId(), "Good morning") 593 .assertViewTextChanged(activity.mPassword.getAutofillId(), "How are you"); 594 595 activity.assertInitialViewsDisappeared(assertor); 596 } 597 598 @Test testComposingSpan_eventsForSpanChanges()599 public void testComposingSpan_eventsForSpanChanges() throws Exception { 600 final CtsContentCaptureService service = enableService(); 601 final ActivityWatcher watcher = startWatcher(); 602 603 LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername 604 .setText("")); 605 606 final LoginActivity activity = launchActivity(); 607 watcher.waitFor(RESUMED); 608 609 activity.syncRunOnUiThread(() -> { 610 activity.mUsername.setText("Android"); 611 final InputConnection inputConnection = 612 activity.mUsername.onCreateInputConnection(new EditorInfo()); 613 614 // These 2 should be merged. 615 inputConnection.setComposingRegion(1, 2); 616 inputConnection.setComposingRegion(1, 3); 617 618 inputConnection.finishComposingText(); 619 activity.mUsername.setText("end"); 620 // TODO: Test setComposingText. 621 }); 622 // wait to sent event 623 sleep(); 624 625 activity.finish(); 626 watcher.waitFor(DESTROYED); 627 628 final Session session = service.getOnlyFinishedSession(); 629 630 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 631 632 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 5) 633 // TODO: The first two events should probably be merged. 634 .assertViewTextChanged(activity.mUsername.getAutofillId(), "Android"); 635 636 assertor.assertViewTextChanged(activity.mUsername.getAutofillId(), "Android"); 637 638 assertor.assertViewTextChanged(activity.mUsername.getAutofillId(), "Android"); 639 640 assertor.assertViewTextChanged(activity.mUsername.getAutofillId(), "end"); 641 642 activity.assertInitialViewsDisappeared(assertor); 643 } 644 appendText(EditText editText, String text)645 private void appendText(EditText editText, String text) { 646 appendText(editText, text, true); 647 } 648 appendText(EditText editText, String text, boolean hasComposingSpan)649 private void appendText(EditText editText, String text, boolean hasComposingSpan) { 650 Editable editable = editText.getText(); 651 String s = editable.toString() + text; 652 Editable newEditable = Editable.Factory.getInstance().newEditable(s); 653 if (hasComposingSpan) { 654 BaseInputConnection.setComposingSpans(newEditable); 655 } else { 656 BaseInputConnection.removeComposingSpans(editable); 657 } 658 editable.replace(0, editable.length() , newEditable); 659 } 660 661 @Test testDisabledByFlagSecure()662 public void testDisabledByFlagSecure() throws Exception { 663 final CtsContentCaptureService service = enableService(); 664 final ActivityWatcher watcher = startWatcher(); 665 666 LoginActivity.onRootView((activity, rootView) -> activity.getWindow() 667 .addFlags(WindowManager.LayoutParams.FLAG_SECURE)); 668 669 final LoginActivity activity = launchActivity(); 670 watcher.waitFor(RESUMED); 671 672 activity.finish(); 673 watcher.waitFor(DESTROYED); 674 675 final Session session = service.getOnlyFinishedSession(); 676 assertThat((session.context.getFlags() 677 & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue(); 678 final ContentCaptureSessionId sessionId = session.id; 679 Log.v(TAG, "session id: " + sessionId); 680 681 assertRightActivity(session, sessionId, activity); 682 683 final List<ContentCaptureEvent> events = session.getEvents(); 684 assertThat(events).isEmpty(); 685 } 686 687 @Test testDisabledByApp()688 public void testDisabledByApp() throws Exception { 689 final CtsContentCaptureService service = enableService(); 690 final ActivityWatcher watcher = startWatcher(); 691 692 LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager() 693 .setContentCaptureEnabled(false)); 694 695 final LoginActivity activity = launchActivity(); 696 watcher.waitFor(RESUMED); 697 698 assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse(); 699 700 activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH")); 701 702 activity.finish(); 703 watcher.waitFor(DESTROYED); 704 705 final Session session = service.getOnlyFinishedSession(); 706 assertThat((session.context.getFlags() 707 & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue(); 708 final ContentCaptureSessionId sessionId = session.id; 709 Log.v(TAG, "session id: " + sessionId); 710 711 assertRightActivity(session, sessionId, activity); 712 713 final List<ContentCaptureEvent> events = session.getEvents(); 714 assertThat(events).isEmpty(); 715 } 716 717 @Test testDisabledFlagSecureAndByApp()718 public void testDisabledFlagSecureAndByApp() throws Exception { 719 final CtsContentCaptureService service = enableService(); 720 final ActivityWatcher watcher = startWatcher(); 721 722 LoginActivity.onRootView((activity, rootView) -> { 723 activity.getContentCaptureManager().setContentCaptureEnabled(false); 724 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); 725 }); 726 727 final LoginActivity activity = launchActivity(); 728 watcher.waitFor(RESUMED); 729 730 assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse(); 731 activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH")); 732 733 activity.finish(); 734 watcher.waitFor(DESTROYED); 735 736 final Session session = service.getOnlyFinishedSession(); 737 assertThat((session.context.getFlags() 738 & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue(); 739 assertThat((session.context.getFlags() 740 & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue(); 741 final ContentCaptureSessionId sessionId = session.id; 742 Log.v(TAG, "session id: " + sessionId); 743 744 assertRightActivity(session, sessionId, activity); 745 746 final List<ContentCaptureEvent> events = session.getEvents(); 747 assertThat(events).isEmpty(); 748 } 749 750 @Test testUserDataRemovalRequest_forEverything()751 public void testUserDataRemovalRequest_forEverything() throws Exception { 752 final CtsContentCaptureService service = enableService(); 753 final ActivityWatcher watcher = startWatcher(); 754 755 LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager() 756 .removeData(new DataRemovalRequest.Builder().forEverything() 757 .build())); 758 759 final LoginActivity activity = launchActivity(); 760 watcher.waitFor(RESUMED); 761 762 activity.finish(); 763 watcher.waitFor(DESTROYED); 764 765 DataRemovalRequest request = service.getRemovalRequest(); 766 assertThat(request).isNotNull(); 767 assertThat(request.isForEverything()).isTrue(); 768 assertThat(request.getLocusIdRequests()).isNull(); 769 assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE); 770 } 771 772 @Test testUserDataRemovalRequest_oneId()773 public void testUserDataRemovalRequest_oneId() throws Exception { 774 final CtsContentCaptureService service = enableService(); 775 final ActivityWatcher watcher = startWatcher(); 776 777 final LocusId locusId = new LocusId("com.example"); 778 779 LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager() 780 .removeData(new DataRemovalRequest.Builder() 781 .addLocusId(locusId, NO_FLAGS) 782 .build())); 783 784 final LoginActivity activity = launchActivity(); 785 watcher.waitFor(RESUMED); 786 787 activity.finish(); 788 watcher.waitFor(DESTROYED); 789 790 DataRemovalRequest request = service.getRemovalRequest(); 791 assertThat(request).isNotNull(); 792 assertThat(request.isForEverything()).isFalse(); 793 assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE); 794 795 final List<LocusIdRequest> requests = request.getLocusIdRequests(); 796 assertThat(requests.size()).isEqualTo(1); 797 798 final LocusIdRequest actualRequest = requests.get(0); 799 assertThat(actualRequest.getLocusId()).isEqualTo(locusId); 800 assertThat(actualRequest.getFlags()).isEqualTo(NO_FLAGS); 801 } 802 803 @Test testUserDataRemovalRequest_manyIds()804 public void testUserDataRemovalRequest_manyIds() throws Exception { 805 final CtsContentCaptureService service = enableService(); 806 final ActivityWatcher watcher = startWatcher(); 807 808 final LocusId locusId1 = new LocusId("com.example"); 809 final LocusId locusId2 = new LocusId("com.example2"); 810 811 LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager() 812 .removeData(new DataRemovalRequest.Builder() 813 .addLocusId(locusId1, NO_FLAGS) 814 .addLocusId(locusId2, FLAG_IS_PREFIX) 815 .build())); 816 817 final LoginActivity activity = launchActivity(); 818 watcher.waitFor(RESUMED); 819 820 activity.finish(); 821 watcher.waitFor(DESTROYED); 822 823 final DataRemovalRequest request = service.getRemovalRequest(); 824 assertThat(request).isNotNull(); 825 assertThat(request.isForEverything()).isFalse(); 826 assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE); 827 828 final List<LocusIdRequest> requests = request.getLocusIdRequests(); 829 assertThat(requests.size()).isEqualTo(2); 830 831 final LocusIdRequest actualRequest1 = requests.get(0); 832 assertThat(actualRequest1.getLocusId()).isEqualTo(locusId1); 833 assertThat(actualRequest1.getFlags()).isEqualTo(NO_FLAGS); 834 835 final LocusIdRequest actualRequest2 = requests.get(1); 836 assertThat(actualRequest2.getLocusId()).isEqualTo(locusId2); 837 assertThat(actualRequest2.getFlags()).isEqualTo(FLAG_IS_PREFIX); 838 } 839 840 @Test testAddChildren_rightAway()841 public void testAddChildren_rightAway() throws Exception { 842 final CtsContentCaptureService service = enableService(); 843 final ActivityWatcher watcher = startWatcher(); 844 final View[] children = new View[2]; 845 846 final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity, 847 rootView) -> { 848 final TextView child1 = newImportantView(activity, "c1"); 849 children[0] = child1; 850 Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1); 851 rootView.addView(child1); 852 final TextView child2 = newImportantView(activity, "c1"); 853 children[1] = child2; 854 Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2); 855 rootView.addView(child2); 856 }; 857 LoginActivity.onRootView(visitor); 858 859 final LoginActivity activity = launchActivity(); 860 watcher.waitFor(RESUMED); 861 862 activity.finish(); 863 watcher.waitFor(DESTROYED); 864 865 final Session session = service.getOnlyFinishedSession(); 866 Log.v(TAG, "session id: " + session.id); 867 868 final ContentCaptureSessionId sessionId = session.id; 869 assertRightActivity(session, sessionId, activity); 870 871 final List<ContentCaptureEvent> events = activity.assertJustInitialViewsAppeared(session, 872 /* additionalEvents= */ 2); 873 final AutofillId rootId = activity.getRootView().getAutofillId(); 874 int i = LoginActivity.MIN_EVENTS - 1; 875 assertViewAppeared(events, i, sessionId, children[0], rootId); 876 assertViewAppeared(events, i + 1, sessionId, children[1], rootId); 877 assertViewTreeFinished(events, i + 2); 878 879 activity.assertInitialViewsDisappeared(events, children.length); 880 } 881 882 @Test testViewAppeared_withNewContext()883 public void testViewAppeared_withNewContext() throws Exception { 884 final CtsContentCaptureService service = enableService(); 885 final ActivityWatcher watcher = startWatcher(); 886 887 final LoginActivity activity = launchActivity(); 888 watcher.waitFor(RESUMED); 889 890 // Add View 891 final LinearLayout rootView = activity.getRootView(); 892 final Context newContext = activity.createContext(new ContextParams.Builder().build()); 893 final TextView child = newImportantView(newContext, "Important I am"); 894 activity.runOnUiThread(() -> rootView.addView(child)); 895 // wait to sent event 896 sleep(); 897 898 activity.finish(); 899 watcher.waitFor(DESTROYED); 900 901 final Session session = service.getOnlyFinishedSession(); 902 final ContentCaptureSessionId sessionId = session.id; 903 Log.v(TAG, "session id: " + sessionId); 904 final AutofillId rootId = activity.getRootView().getAutofillId(); 905 906 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 907 908 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 3) 909 .assertViewTreeStarted() 910 .assertViewAppeared(sessionId, child, rootId) 911 .assertViewTreeFinished(); 912 } 913 914 @Test testAddChildren_afterAnimation()915 public void testAddChildren_afterAnimation() throws Exception { 916 final CtsContentCaptureService service = enableService(); 917 final ActivityWatcher watcher = startWatcher(); 918 final View[] children = new View[2]; 919 920 final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity, 921 rootView) -> { 922 final TextView child1 = newImportantView(activity, "c1"); 923 children[0] = child1; 924 Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1); 925 rootView.addView(child1); 926 final TextView child2 = newImportantView(activity, "c1"); 927 children[1] = child2; 928 Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2); 929 rootView.addView(child2); 930 }; 931 LoginActivity.onAnimationComplete(visitor); 932 933 final LoginActivity activity = launchActivity(); 934 watcher.waitFor(RESUMED); 935 936 // Wait to make sure the animation is complete 937 sleep(); 938 939 activity.finish(); 940 watcher.waitFor(DESTROYED); 941 942 final Session session = service.getOnlyFinishedSession(); 943 Log.v(TAG, "session id: " + session.id); 944 final ContentCaptureSessionId sessionId = session.id; 945 final View decorView = activity.getDecorView(); 946 final View grandpa1 = activity.getGrandParent(); 947 final View grandpa2 = activity.getGrandGrandParent(); 948 final AutofillId rootId = activity.getRootView().getAutofillId(); 949 950 final EventsAssertor assertor = activity.assertInitialViewsAppeared(session); 951 952 assertor.isAtLeast(LoginActivity.MIN_EVENTS + 5) 953 .assertViewTreeStarted() 954 .assertViewAppeared(sessionId, children[0], rootId) 955 .assertViewAppeared(sessionId, children[1], rootId) 956 .assertViewTreeFinished() 957 .assertViewDisappeared( 958 decorView.getAutofillId(), 959 grandpa1.getAutofillId(), grandpa2.getAutofillId(), 960 rootId, 961 activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(), 962 activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId(), 963 children[0].getAutofillId(), children[1].getAutofillId()); 964 } 965 sleep()966 private void sleep() { 967 Log.d(TAG, "sleeping 0.5s "); 968 SystemClock.sleep(500); 969 } 970 971 @Test testWhitelist_packageNotWhitelisted()972 public void testWhitelist_packageNotWhitelisted() throws Exception { 973 final CtsContentCaptureService service = enableService(); 974 final ActivityWatcher watcher = startWatcher(); 975 976 service.setContentCaptureWhitelist((Set) null, (Set) null); 977 978 final LoginActivity activity = launchActivity(); 979 watcher.waitFor(RESUMED); 980 981 activity.finish(); 982 watcher.waitFor(DESTROYED); 983 984 assertThat(service.getAllSessionIds()).isEmpty(); 985 } 986 987 @Test testWhitelist_activityNotWhitelisted()988 public void testWhitelist_activityNotWhitelisted() throws Exception { 989 final CtsContentCaptureService service = enableService(); 990 final ArraySet<ComponentName> components = new ArraySet<>(); 991 components.add(new ComponentName(MY_PACKAGE, "some.activity")); 992 service.setContentCaptureWhitelist(null, components); 993 final ActivityWatcher watcher = startWatcher(); 994 995 final LoginActivity activity = launchActivity(); 996 watcher.waitFor(RESUMED); 997 998 activity.finish(); 999 watcher.waitFor(DESTROYED); 1000 1001 assertThat(service.getAllSessionIds()).isEmpty(); 1002 } 1003 1004 /** 1005 * Creates a context that can be assert by 1006 * {@link #assertContentCaptureContext(ContentCaptureContext)}. 1007 */ newContentCaptureContext()1008 private ContentCaptureContext newContentCaptureContext() { 1009 final String id = "file://dev/null"; 1010 final Bundle bundle = new Bundle(); 1011 bundle.putString("DUDE", "SWEET"); 1012 return new ContentCaptureContext.Builder(new LocusId(id)).setExtras(bundle).build(); 1013 } 1014 1015 /** 1016 * Asserts a context that can has been created by {@link #newContentCaptureContext()}. 1017 */ assertContentCaptureContext(@onNull ContentCaptureContext context)1018 private void assertContentCaptureContext(@NonNull ContentCaptureContext context) { 1019 assertWithMessage("null context").that(context).isNotNull(); 1020 assertWithMessage("wrong ID on context %s", context).that(context.getLocusId().getId()) 1021 .isEqualTo("file://dev/null"); 1022 final Bundle extras = context.getExtras(); 1023 assertWithMessage("no extras on context %s", context).that(extras).isNotNull(); 1024 assertWithMessage("wrong number of extras on context %s", context).that(extras.size()) 1025 .isEqualTo(1); 1026 assertWithMessage("wrong extras on context %s", context).that(extras.getString("DUDE")) 1027 .isEqualTo("SWEET"); 1028 } 1029 1030 // TODO(b/123540602): add moar test cases for different sessions: 1031 // - session1 on rootView, session2 on children 1032 // - session1 on rootView, session2 on child1, session3 on child2 1033 // - combination above where the CTS test explicitly finishes a session 1034 1035 // TODO(b/123540602): add moar test cases for different scenarios, like: 1036 // - dynamically adding / 1037 // - removing views 1038 // - pausing / resuming activity / tapping home 1039 // - changing text 1040 // - secure flag with child sessions 1041 // - making sure events are flushed when activity pause / resume 1042 1043 // TODO(b/126262658): moar lifecycle events, like multiple activities. 1044 1045 } 1046