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 package android.contentcaptureservice.cts; 17 18 import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.CREATION; 19 import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.DESTRUCTION; 20 import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext; 21 import static android.contentcaptureservice.cts.Assertions.assertLifecycleOrder; 22 import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext; 23 import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents; 24 import static android.contentcaptureservice.cts.Assertions.assertRightActivity; 25 import static android.contentcaptureservice.cts.Assertions.assertSessionFlush; 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.assertViewDisappeared; 30 import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished; 31 import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted; 32 import static android.contentcaptureservice.cts.Assertions.assertViewsDisappeared; 33 import static android.contentcaptureservice.cts.Assertions.removeUnexpectedEvents; 34 import static android.contentcaptureservice.cts.Helper.newImportantView; 35 import static android.contentcaptureservice.cts.Helper.sContext; 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 42 import static org.testng.Assert.assertThrows; 43 44 import android.content.ComponentName; 45 import android.content.LocusId; 46 import android.contentcaptureservice.cts.CtsContentCaptureService.DisconnectListener; 47 import android.contentcaptureservice.cts.CtsContentCaptureService.ServiceWatcher; 48 import android.contentcaptureservice.cts.CtsContentCaptureService.Session; 49 import android.os.SystemClock; 50 import android.platform.test.annotations.AppModeFull; 51 import android.platform.test.annotations.RequiresFlagsDisabled; 52 import android.platform.test.annotations.RequiresFlagsEnabled; 53 import android.platform.test.flag.junit.CheckFlagsRule; 54 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 55 import android.util.Log; 56 import android.view.View; 57 import android.view.autofill.AutofillId; 58 import android.view.contentcapture.ContentCaptureContext; 59 import android.view.contentcapture.ContentCaptureEvent; 60 import android.view.contentcapture.ContentCaptureManager; 61 import android.view.contentcapture.ContentCaptureSession; 62 import android.view.contentcapture.ContentCaptureSessionId; 63 import android.view.contentcapture.flags.Flags; 64 import android.widget.LinearLayout; 65 import android.widget.TextView; 66 67 import androidx.annotation.NonNull; 68 import androidx.annotation.Nullable; 69 import androidx.test.rule.ActivityTestRule; 70 71 import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher; 72 import com.android.compatibility.common.util.ActivityLauncher; 73 74 import org.junit.After; 75 import org.junit.Before; 76 import org.junit.Rule; 77 import org.junit.Test; 78 79 import java.util.Arrays; 80 import java.util.List; 81 import java.util.concurrent.atomic.AtomicReference; 82 83 @AppModeFull(reason = "BlankWithTitleActivityTest is enough") 84 public class ChildlessActivityTest 85 extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<ChildlessActivity> { 86 87 private static final String TAG = ChildlessActivityTest.class.getSimpleName(); 88 89 private static final ActivityTestRule<ChildlessActivity> sActivityRule = new ActivityTestRule<>( 90 ChildlessActivity.class, false, false); 91 92 @Rule 93 public final CheckFlagsRule mCheckFlagsRule = 94 DeviceFlagsValueProvider.createCheckFlagsRule(); 95 96 ChildlessActivityTest()97 public ChildlessActivityTest() { 98 super(ChildlessActivity.class); 99 } 100 101 @Override getActivityTestRule()102 protected ActivityTestRule<ChildlessActivity> getActivityTestRule() { 103 return sActivityRule; 104 } 105 106 @Before 107 @After resetActivityStaticState()108 public void resetActivityStaticState() { 109 ChildlessActivity.onRootView(null); 110 } 111 112 @Test testDefaultLifecycle()113 public void testDefaultLifecycle() throws Exception { 114 final CtsContentCaptureService service = enableService(); 115 final ActivityWatcher watcher = startWatcher(); 116 117 final ChildlessActivity activity = launchActivity(); 118 watcher.waitFor(RESUMED); 119 120 activity.finish(); 121 watcher.waitFor(DESTROYED); 122 123 final Session session = service.getOnlyFinishedSession(); 124 Log.v(TAG, "session id: " + session.id); 125 126 activity.assertDefaultEvents(session); 127 } 128 129 @Test testGetContentCapture_disabledWhenNoService()130 public void testGetContentCapture_disabledWhenNoService() throws Exception { 131 final ActivityWatcher watcher = startWatcher(); 132 133 final ChildlessActivity activity = launchActivity(); 134 watcher.waitFor(RESUMED); 135 136 assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse(); 137 138 activity.finish(); 139 watcher.waitFor(DESTROYED); 140 } 141 142 @Test testGetContentCapture_enabledWhenNoService()143 public void testGetContentCapture_enabledWhenNoService() throws Exception { 144 enableService(); 145 final ActivityWatcher watcher = startWatcher(); 146 147 final ChildlessActivity activity = launchActivity(); 148 watcher.waitFor(RESUMED); 149 150 assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isTrue(); 151 152 activity.finish(); 153 watcher.waitFor(DESTROYED); 154 155 } 156 157 @Test testLaunchAnotherActivity()158 public void testLaunchAnotherActivity() throws Exception { 159 final CtsContentCaptureService service = enableService(); 160 final ActivityWatcher watcher1 = startWatcher(); 161 162 // Launch and finish 1st activity 163 final ChildlessActivity activity1 = launchActivity(); 164 watcher1.waitFor(RESUMED); 165 activity1.finish(); 166 watcher1.waitFor(DESTROYED); 167 168 // Launch and finish 2nd activity 169 final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>( 170 sContext, mActivitiesWatcher, LoginActivity.class); 171 final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher(); 172 final LoginActivity activity2 = anotherActivityLauncher.launchActivity(); 173 watcher2.waitFor(RESUMED); 174 activity2.finish(); 175 watcher2.waitFor(DESTROYED); 176 177 // Assert the sessions 178 final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds(); 179 assertThat(sessionIds).hasSize(2); 180 final ContentCaptureSessionId sessionId1 = sessionIds.get(0); 181 Log.v(TAG, "session id1: " + sessionId1); 182 final ContentCaptureSessionId sessionId2 = sessionIds.get(1); 183 Log.v(TAG, "session id2: " + sessionId2); 184 185 final Session session1 = service.getFinishedSession(sessionId1); 186 activity1.assertDefaultEvents(session1); 187 188 final Session session2 = service.getFinishedSession(sessionId2); 189 activity2.assertDefaultEvents(session2); 190 } 191 192 @Test testLaunchAnotherActivity_onTopOfIt()193 public void testLaunchAnotherActivity_onTopOfIt() throws Exception { 194 final CtsContentCaptureService service = enableService(); 195 final ActivityWatcher watcher1 = startWatcher(); 196 197 // Launch 1st activity 198 final ChildlessActivity activity1 = launchActivity(); 199 watcher1.waitFor(RESUMED); 200 // The task id will be -1 if the Activity is finished 201 final int taskId1 = activity1.getTaskId(); 202 203 // Launch and finish 2nd activity 204 final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>( 205 sContext, mActivitiesWatcher, LoginActivity.class); 206 final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher(); 207 final LoginActivity activity2 = anotherActivityLauncher.launchActivity(); 208 209 watcher2.waitFor(RESUMED); 210 final int taskId2 = activity2.getTaskId(); 211 activity2.finish(); 212 watcher2.waitFor(DESTROYED); 213 214 // Finish 1st activity 215 activity1.finish(); 216 watcher1.waitFor(DESTROYED); 217 218 // Assert the activity lifecycle events 219 final ComponentName name1 = activity1.getComponentName(); 220 final ComponentName name2 = activity2.getComponentName(); 221 service.assertThat() 222 .activityResumed(name1, taskId1) 223 .activityPaused(name1, taskId1) 224 .activityResumed(name2, taskId2) 225 .activityPaused(name2, taskId2) 226 .activityResumed(name1, taskId1) 227 .activityPaused(name1, taskId1); 228 229 // Assert the sessions 230 final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds(); 231 assertThat(sessionIds).hasSize(2); 232 final ContentCaptureSessionId sessionId1 = sessionIds.get(0); 233 Log.v(TAG, "session id1: " + sessionId1); 234 final ContentCaptureSessionId sessionId2 = sessionIds.get(1); 235 Log.v(TAG, "session id2: " + sessionId2); 236 237 final Session session1 = service.getFinishedSession(sessionId1); 238 final List<ContentCaptureEvent> events1 = removeUnexpectedEvents(session1.getEvents()); 239 Log.v(TAG, "events on " + activity1 + ": " + events1); 240 assertThat(events1).hasSize(4); 241 assertSessionResumed(events1, 0); 242 assertSessionPaused(events1, 1); 243 assertSessionResumed(events1, 2); 244 assertSessionPaused(events1, 3); 245 246 final Session session2 = service.getFinishedSession(sessionId2); 247 activity2.assertDefaultEvents(session2); 248 249 } 250 251 @Test testAddAndRemoveNoImportantChild()252 public void testAddAndRemoveNoImportantChild() throws Exception { 253 final CtsContentCaptureService service = enableService(); 254 final ActivityWatcher watcher = startWatcher(); 255 256 // Child must be created inside the lambda because it needs to use the Activity context. 257 final AtomicReference<TextView> childRef = new AtomicReference<>(); 258 259 ChildlessActivity.onRootView((activity, rootView) -> { 260 final TextView child = new TextView(activity); 261 child.setText("VIEW, Y U NO IMPORTANT?"); 262 child.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO); 263 264 rootView.addView(child); 265 childRef.set(child); 266 }); 267 268 final ChildlessActivity activity = launchActivity(); 269 watcher.waitFor(RESUMED); 270 271 // Remove view 272 final TextView child = childRef.get(); 273 activity.syncRunOnUiThread(() -> activity.getRootView().removeView(child)); 274 275 activity.finish(); 276 watcher.waitFor(DESTROYED); 277 278 final Session session = service.getOnlyFinishedSession(); 279 final ContentCaptureSessionId sessionId = session.id; 280 Log.v(TAG, "session id: " + sessionId); 281 282 assertRightActivity(session, sessionId, activity); 283 284 // Should be empty because the root view is not important for content capture without a 285 // child that is important. 286 assertNoViewLevelEvents(session, activity); 287 } 288 289 @Test testAddAndRemoveImportantChild()290 public void testAddAndRemoveImportantChild() throws Exception { 291 final CtsContentCaptureService service = enableService(); 292 final ActivityWatcher watcher = startWatcher(); 293 294 // TODO(b/120494182): Child must be created inside the lambda because it needs to use the 295 // Activity context. 296 final AtomicReference<TextView> childRef = new AtomicReference<>(); 297 298 ChildlessActivity.onRootView((activity, rootView) -> { 299 final TextView text = newImportantView(activity, "Important I am"); 300 rootView.addView(text); 301 childRef.set(text); 302 }); 303 304 final ChildlessActivity activity = launchActivity(); 305 watcher.waitFor(RESUMED); 306 307 // Remove view 308 final LinearLayout rootView = activity.getRootView(); 309 final TextView child = childRef.get(); 310 activity.syncRunOnUiThread(() -> rootView.removeView(child)); 311 312 activity.finish(); 313 watcher.waitFor(DESTROYED); 314 315 final Session session = service.getOnlyFinishedSession(); 316 final ContentCaptureSessionId sessionId = session.id; 317 Log.v(TAG, "session id: " + sessionId); 318 319 assertRightActivity(session, sessionId, activity); 320 321 final List<ContentCaptureEvent> events = session.getEvents(); 322 Log.v(TAG, "events(" + events.size() + "): " + events); 323 324 final AutofillId rootId = rootView.getAutofillId(); 325 326 final View grandpa1 = activity.getGrandParent(); 327 final View grandpa2 = activity.getGrandGrandParent(); 328 final View decorView = activity.getDecorView(); 329 330 new EventsAssertor(events) 331 .isAtLeast(12) 332 .assertSessionResumed() 333 .assertViewTreeStarted() 334 .assertDecorViewAppeared(decorView) 335 .assertViewAppeared(grandpa2, decorView.getAutofillId()) 336 .assertViewAppeared(grandpa1, grandpa2.getAutofillId()) 337 .assertViewAppeared(sessionId, rootView, grandpa1.getAutofillId()) 338 .assertViewAppeared(sessionId, child, rootId) 339 .assertViewTreeFinished() 340 .assertViewTreeStarted() 341 .assertViewDisappeared(child.getAutofillId()) 342 .assertViewTreeFinished() 343 .assertSessionPaused(); 344 345 // TODO(b/122315042): assert parents disappeared 346 } 347 348 @Test testAddImportantChildAfterSessionStarted()349 public void testAddImportantChildAfterSessionStarted() throws Exception { 350 final CtsContentCaptureService service = enableService(); 351 final ActivityWatcher watcher = startWatcher(); 352 353 final ChildlessActivity activity = launchActivity(); 354 watcher.waitFor(RESUMED); 355 356 // Add View 357 final LinearLayout rootView = activity.getRootView(); 358 final TextView child = newImportantView(activity, "Important I am"); 359 activity.runOnUiThread(() -> rootView.addView(child)); 360 361 activity.finish(); 362 watcher.waitFor(DESTROYED); 363 364 final Session session = service.getOnlyFinishedSession(); 365 final ContentCaptureSessionId sessionId = session.id; 366 Log.v(TAG, "session id: " + sessionId); 367 368 assertRightActivity(session, sessionId, activity); 369 370 final List<ContentCaptureEvent> events = session.getEvents(); 371 Log.v(TAG, "events(" + events.size() + "): " + events); 372 373 final View grandpa = activity.getGrandParent(); 374 final View grandpa2 = (View) grandpa.getParent(); 375 final View decor = activity.getDecorView(); 376 377 // Assert just the relevant events 378 new EventsAssertor(events) 379 .isAtLeast(9) 380 .assertSessionResumed() 381 .assertViewTreeStarted() 382 .assertDecorViewAppeared(decor) 383 .assertViewAppeared(sessionId, grandpa2, decor.getAutofillId()) 384 .assertViewAppeared(sessionId, grandpa, grandpa2.getAutofillId()) 385 .assertViewAppeared(sessionId, rootView, grandpa.getAutofillId()) 386 .assertViewAppeared(sessionId, child, rootView.getAutofillId()) 387 .assertViewTreeFinished() 388 .assertSessionPaused(); 389 } 390 391 @Test testAddAndRemoveImportantChildOnDifferentSession()392 public void testAddAndRemoveImportantChildOnDifferentSession() throws Exception { 393 final CtsContentCaptureService service = enableService(); 394 final ActivityWatcher watcher = startWatcher(); 395 396 final ChildlessActivity activity = launchActivity(); 397 watcher.waitFor(RESUMED); 398 399 final LinearLayout rootView = activity.getRootView(); 400 final View grandpa = activity.getGrandParent(); 401 final View grandpa2 = (View) grandpa.getParent(); 402 final View decor = activity.getDecorView(); 403 404 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 405 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 406 Log.v(TAG, "main session id: " + mainSessionId); 407 408 final ContentCaptureSession childSession = mainSession 409 .createContentCaptureSession(newContentCaptureContextBuilder("child") 410 .build()); 411 final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId(); 412 Log.v(TAG, "child session id: " + childSessionId); 413 414 final TextView child = newImportantView(activity, "Important I am"); 415 final AutofillId childId = child.getAutofillId(); 416 Log.v(TAG, "childId: " + childId); 417 child.setContentCaptureSession(childSession); 418 activity.runOnUiThread(() -> rootView.addView(child)); 419 420 activity.finish(); 421 watcher.waitFor(DESTROYED); 422 423 final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds(); 424 assertThat(sessionIds).containsExactly(mainSessionId, childSessionId).inOrder(); 425 426 // Assert sessions 427 final Session mainTestSession = service.getFinishedSession(mainSessionId); 428 assertMainSessionContext(mainTestSession, activity); 429 final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents(); 430 Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents); 431 432 new EventsAssertor(mainEvents) 433 .isAtLeast(8) 434 .assertSessionResumed() 435 .assertViewTreeStarted() 436 .assertDecorViewAppeared(decor) 437 .assertViewAppeared(mainSessionId, grandpa2, decor.getAutofillId()) 438 .assertViewAppeared(mainSessionId, grandpa, grandpa2.getAutofillId()) 439 .assertViewAppeared(mainSessionId, rootView, grandpa.getAutofillId()) 440 .assertViewTreeFinished() 441 .assertSessionPaused(); 442 443 final Session childTestSession = service.getFinishedSession(childSessionId); 444 assertChildSessionContext(childTestSession, "child"); 445 final List<ContentCaptureEvent> childEvents = childTestSession.getEvents(); 446 Log.v(TAG, "childEvents(" + childEvents.size() + "): " + childEvents); 447 448 new EventsAssertor(childEvents) 449 .isAtLeast(3) 450 .assertViewTreeStarted() 451 .assertViewAppeared(childSessionId, child, rootView.getAutofillId()) 452 .assertViewTreeFinished(); 453 454 // TODO(b/122315042): assert parents disappeared 455 } 456 457 /** 458 * Tests scenario where new sessions are added from the main session, but they're not nested 459 * neither have views attached to them. 460 */ 461 @Test testDinamicallyManageChildlessSiblingSessions()462 public void testDinamicallyManageChildlessSiblingSessions() throws Exception { 463 final CtsContentCaptureService service = enableService(); 464 final ActivityWatcher watcher = startWatcher(); 465 466 final ChildlessActivity activity = launchActivity(); 467 watcher.waitFor(RESUMED); 468 469 final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession(); 470 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 471 Log.v(TAG, "main session id: " + mainSessionId); 472 473 // Create 1st session 474 final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1") 475 .build(); 476 final ContentCaptureSession childSession1 = mainSession 477 .createContentCaptureSession(context1); 478 final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId(); 479 Log.v(TAG, "child session id 1: " + childSessionId1); 480 481 // Create 2nd session 482 final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2") 483 .build(); 484 final ContentCaptureSession childSession2 = mainSession 485 .createContentCaptureSession(context2); 486 final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId(); 487 Log.v(TAG, "child session id 2: " + childSessionId2); 488 489 // Close 1st session before opening 3rd 490 childSession1.close(); 491 492 // Create 3nd session... 493 final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3") 494 .build(); 495 final ContentCaptureSession childSession3 = mainSession 496 .createContentCaptureSession(context3); 497 final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId(); 498 Log.v(TAG, "child session id 3: " + childSessionId3); 499 500 // ...and close it right away 501 childSession3.close(); 502 503 // Create 4nd session 504 final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4") 505 .build(); 506 final ContentCaptureSession childSession4 = mainSession 507 .createContentCaptureSession(context4); 508 final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId(); 509 Log.v(TAG, "child session id 4: " + childSessionId4); 510 511 activity.finish(); 512 watcher.waitFor(DESTROYED); 513 514 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 515 assertThat(receivedIds).containsExactly( 516 mainSessionId, 517 childSessionId1, 518 childSessionId2, 519 childSessionId3, 520 childSessionId4) 521 .inOrder(); 522 523 // Assert main sessions info 524 final Session mainTestSession = service.getFinishedSession(mainSessionId); 525 assertMainSessionContext(mainTestSession, activity); 526 527 final Session childTestSession1 = service.getFinishedSession(childSessionId1); 528 assertChildSessionContext(childTestSession1, "session1"); 529 530 final Session childTestSession2 = service.getFinishedSession(childSessionId2); 531 assertChildSessionContext(childTestSession2, "session2"); 532 533 final Session childTestSession3 = service.getFinishedSession(childSessionId3); 534 assertChildSessionContext(childTestSession3, "session3"); 535 536 final Session childTestSession4 = service.getFinishedSession(childSessionId4); 537 assertChildSessionContext(childTestSession4, "session4"); 538 539 // Gets all events first so they're all logged before the assertions 540 final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents(); 541 final List<ContentCaptureEvent> events1 = childTestSession1.getEvents(); 542 final List<ContentCaptureEvent> events2 = childTestSession2.getEvents(); 543 final List<ContentCaptureEvent> events3 = childTestSession3.getEvents(); 544 final List<ContentCaptureEvent> events4 = childTestSession4.getEvents(); 545 Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents); 546 Log.v(TAG, "events1(" + events1.size() + "): " + events1); 547 Log.v(TAG, "events2(" + events2.size() + "): " + events2); 548 Log.v(TAG, "events3(" + events3.size() + "): " + events3); 549 Log.v(TAG, "events4(" + events4.size() + "): " + events4); 550 551 assertNoViewLevelEvents(mainTestSession, activity); 552 assertThat(events1).isEmpty(); 553 assertThat(events2).isEmpty(); 554 assertThat(events3).isEmpty(); 555 assertThat(events4).isEmpty(); 556 557 // Assert lifecycle methods were called in the right order 558 assertLifecycleOrder(1, mainTestSession, CREATION); 559 assertLifecycleOrder(2, childTestSession1, CREATION); 560 assertLifecycleOrder(3, childTestSession2, CREATION); 561 assertLifecycleOrder(4, childTestSession1, DESTRUCTION); 562 assertLifecycleOrder(5, childTestSession3, CREATION); 563 assertLifecycleOrder(6, childTestSession3, DESTRUCTION); 564 assertLifecycleOrder(7, childTestSession4, CREATION); 565 assertLifecycleOrder(8, childTestSession2, DESTRUCTION); 566 assertLifecycleOrder(9, childTestSession4, DESTRUCTION); 567 assertLifecycleOrder(10, mainTestSession, DESTRUCTION); 568 } 569 570 @Test testDinamicallyAddOneChildOnAnotherSession_manuallyCloseSession()571 public void testDinamicallyAddOneChildOnAnotherSession_manuallyCloseSession() throws Exception { 572 dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ true); 573 } 574 575 @Test testDinamicallyAddOneChildOnAnotherSession_autoCloseSession()576 public void testDinamicallyAddOneChildOnAnotherSession_autoCloseSession() throws Exception { 577 dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ false); 578 } 579 580 /** 581 * Tests scenario where just 1 session with 1 dinamically added view is created. 582 */ dinamicallyAddOneChildOnAnotherSessionTest(boolean manuallyCloseSession)583 private void dinamicallyAddOneChildOnAnotherSessionTest(boolean manuallyCloseSession) 584 throws Exception { 585 final CtsContentCaptureService service = enableService(); 586 final ActivityWatcher watcher = startWatcher(); 587 588 final ChildlessActivity activity = launchActivity(); 589 watcher.waitFor(RESUMED); 590 final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession(); 591 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 592 Log.v(TAG, "main session id: " + mainSessionId); 593 594 // Create session 595 final ContentCaptureSession childSession = mainSession 596 .createContentCaptureSession( 597 newContentCaptureContextBuilder("child_session").build()); 598 final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId(); 599 Log.v(TAG, "child session: " + childSessionId); 600 601 final TextView child = addChild(activity, childSession, "Sweet O'Mine"); 602 if (manuallyCloseSession) { 603 waitAndClose(childSession); 604 } 605 606 activity.finish(); 607 watcher.waitFor(DESTROYED); 608 609 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 610 assertThat(receivedIds).containsExactly(mainSessionId, childSessionId).inOrder(); 611 612 // Assert main session 613 final Session mainTestSession = service.getFinishedSession(mainSessionId); 614 assertMainSessionContext(mainTestSession, activity); 615 // TODO(b/123540067): ideally it should be empty, but has intermediate parents stuff... 616 // assertThat(mainTestSession.getEvents()).isEmpty(); 617 618 // Assert child session 619 final Session childTestSession = service.getFinishedSession(childSessionId); 620 assertChildSessionContext(childTestSession, "child_session"); 621 final List<ContentCaptureEvent> childEvents = childTestSession.getEvents(); 622 assertThat(childEvents.size()).isAtLeast(3); 623 final AutofillId rootId = activity.getRootView().getAutofillId(); 624 assertViewTreeStarted(childEvents, 0); 625 assertViewAppeared(childEvents, 1, child, rootId); 626 assertViewTreeFinished(childEvents, 2); 627 628 // Assert lifecycle methods were called in the right order 629 assertLifecycleOrder(1, mainTestSession, CREATION); 630 assertLifecycleOrder(2, childTestSession, CREATION); 631 assertLifecycleOrder(3, childTestSession, DESTRUCTION); 632 assertLifecycleOrder(4, mainTestSession, DESTRUCTION); 633 } 634 635 /** 636 * Tests scenario where new sessions with children are added from the main session. 637 */ 638 @Test testDinamicallyManageSiblingSessions()639 public void testDinamicallyManageSiblingSessions() throws Exception { 640 final CtsContentCaptureService service = enableService(); 641 final ActivityWatcher watcher = startWatcher(); 642 643 final ChildlessActivity activity = launchActivity(); 644 watcher.waitFor(RESUMED); 645 final LinearLayout rootView = activity.getRootView(); 646 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 647 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 648 Log.v(TAG, "main session id: " + mainSessionId); 649 650 // Create 1st session 651 final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1") 652 .build(); 653 final ContentCaptureSession childSession1 = mainSession 654 .createContentCaptureSession(context1); 655 final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId(); 656 Log.v(TAG, "child session id 1: " + childSessionId1); 657 658 // Session 1, child 1 659 final TextView s1c1 = addChild(activity, childSession1, "s1c1"); 660 661 // Create 2nd session 662 final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2") 663 .build(); 664 final ContentCaptureSession childSession2 = mainSession 665 .createContentCaptureSession(context2); 666 final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId(); 667 Log.v(TAG, "child session id 2: " + childSessionId2); 668 669 final TextView s2c1 = newImportantView(activity, childSession2, "s2c1"); 670 final TextView s2c2 = newImportantView(activity, childSession2, "s2c1"); 671 672 // Add 2 children together so they're wrapped a view_tree batch 673 activity.runOnUiThread(() -> { 674 rootView.addView(s2c1); 675 rootView.addView(s2c2); 676 }); 677 678 // Close 1st session before opening 3rd 679 waitAndClose(childSession1); 680 681 // Create 3nd session... 682 final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3") 683 .build(); 684 final ContentCaptureSession childSession3 = mainSession 685 .createContentCaptureSession(context3); 686 final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId(); 687 Log.v(TAG, "child session id 3: " + childSessionId3); 688 689 final TextView s3c1 = newImportantView(activity, childSession3, "s3c1"); 690 final TextView s3c2 = newImportantView(activity, childSession3, "s3c1"); 691 final TextView s3c3 = newImportantView(activity, childSession3, "s3c3"); 692 693 // Add 2 children together so they're wrapped a view_tree batch 694 activity.runOnUiThread(() -> { 695 rootView.addView(s3c1); 696 rootView.addView(s3c2); 697 }); 698 699 // TODO(b/123024698): need to wait until the 4 events are flushed - ideally we should block 700 // waiting until the service received them 701 sleep(); 702 703 // Add 2 children so they're wrapped a view_tree batch 704 activity.runOnUiThread(() -> { 705 rootView.removeView(s3c1); 706 rootView.addView(s3c3); 707 }); 708 709 // ...and close it right away 710 waitAndClose(childSession3); 711 712 // Create 4nd session 713 final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4") 714 .build(); 715 final ContentCaptureSession childSession4 = mainSession 716 .createContentCaptureSession(context4); 717 final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId(); 718 Log.v(TAG, "child session id 4: " + childSessionId4); 719 720 activity.finish(); 721 watcher.waitFor(DESTROYED); 722 723 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 724 assertThat(receivedIds).containsExactly( 725 mainSessionId, 726 childSessionId1, 727 childSessionId2, 728 childSessionId3, 729 childSessionId4) 730 .inOrder(); 731 732 // Assert main sessions info 733 final Session mainTestSession = service.getFinishedSession(mainSessionId); 734 assertMainSessionContext(mainTestSession, activity); 735 final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents(); 736 Log.v(TAG, "main session events(" + mainEvents.size() + "): " + mainEvents); 737 738 // Gets all events first so they're all logged before the assertions 739 final Session childTestSession1 = service.getFinishedSession(childSessionId1); 740 assertChildSessionContext(childTestSession1, "session1"); 741 final List<ContentCaptureEvent> events1 = childTestSession1.getEvents(); 742 Log.v(TAG, "events1(" + events1.size() + "): " + events1); 743 744 final Session childTestSession2 = service.getFinishedSession(childSessionId2); 745 final List<ContentCaptureEvent> events2 = childTestSession2.getEvents(); 746 assertChildSessionContext(childTestSession2, "session2"); 747 Log.v(TAG, "events2(" + events2.size() + "): " + events2); 748 final Session childTestSession3 = service.getFinishedSession(childSessionId3); 749 assertChildSessionContext(childTestSession3, "session3"); 750 List<ContentCaptureEvent> events3 = childTestSession3.getEvents(); 751 Log.v(TAG, "events3(" + events3.size() + "): " + events3); 752 753 final AutofillId rootId = rootView.getAutofillId(); 754 final View grandpa = activity.getGrandParent(); 755 final View grandpa2 = (View) grandpa.getParent(); 756 final View decor = activity.getDecorView(); 757 758 new EventsAssertor(mainEvents) 759 .assertSessionResumed() 760 .assertViewTreeStarted() 761 .assertDecorViewAppeared(decor) 762 .assertViewAppeared(grandpa2, decor.getAutofillId()) 763 .assertViewAppeared(grandpa, grandpa2.getAutofillId()) 764 .assertViewAppeared(rootView, grandpa.getAutofillId()) 765 .assertViewTreeFinished() 766 .assertSessionPaused(); 767 768 new EventsAssertor(events1) 769 .isAtLeast(3) 770 .assertViewTreeStarted() 771 .assertViewAppeared(s1c1, rootId) 772 .assertViewTreeFinished(); 773 774 new EventsAssertor(events2) 775 .isAtLeast(4) 776 .assertViewTreeStarted() 777 .assertViewAppeared(s2c1, rootId) 778 .assertViewAppeared(s2c2, rootId) 779 .assertViewTreeFinished(); 780 781 new EventsAssertor(events3) 782 .isAtLeast(8) 783 .assertViewTreeStarted() 784 .assertViewAppeared(s3c1, rootId) 785 .assertViewAppeared(s3c2, rootId) 786 .assertViewTreeFinished() 787 .assertViewTreeStarted() 788 .assertViewDisappeared(s3c1.getAutofillId()) 789 .assertViewAppeared(s3c3, rootId) 790 .assertViewTreeFinished(); 791 792 final Session childTestSession4 = service.getFinishedSession(childSessionId4); 793 assertChildSessionContext(childTestSession4, "session4"); 794 assertThat(childTestSession4.getEvents()).isEmpty(); 795 796 // Assert lifecycle methods were called in the right order 797 assertLifecycleOrder(1, mainTestSession, CREATION); 798 assertLifecycleOrder(2, childTestSession1, CREATION); 799 assertLifecycleOrder(3, childTestSession2, CREATION); 800 assertLifecycleOrder(4, childTestSession1, DESTRUCTION); 801 assertLifecycleOrder(5, childTestSession3, CREATION); 802 assertLifecycleOrder(6, childTestSession3, DESTRUCTION); 803 assertLifecycleOrder(7, childTestSession4, CREATION); 804 assertLifecycleOrder(8, childTestSession2, DESTRUCTION); 805 assertLifecycleOrder(9, childTestSession4, DESTRUCTION); 806 assertLifecycleOrder(10, mainTestSession, DESTRUCTION); 807 } 808 809 @Test testNestedSessions_simplestScenario()810 public void testNestedSessions_simplestScenario() throws Exception { 811 final CtsContentCaptureService service = enableService(); 812 final ActivityWatcher watcher = startWatcher(); 813 814 final ChildlessActivity activity = launchActivity(); 815 watcher.waitFor(RESUMED); 816 817 final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession(); 818 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 819 Log.v(TAG, "main session id: " + mainSessionId); 820 821 // Create child session 822 final ContentCaptureContext childContext = newContentCaptureContextBuilder("child") 823 .build(); 824 final ContentCaptureSession childSession = mainSession 825 .createContentCaptureSession(childContext); 826 final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId(); 827 Log.v(TAG, "child session id: " + childSessionId); 828 829 // Create grand child session 830 final ContentCaptureContext grandChild = newContentCaptureContextBuilder("grandChild") 831 .build(); 832 final ContentCaptureSession grandChildSession = childSession 833 .createContentCaptureSession(grandChild); 834 final ContentCaptureSessionId grandChildSessionId = grandChildSession 835 .getContentCaptureSessionId(); 836 Log.v(TAG, "child session id: " + grandChildSessionId); 837 838 activity.finish(); 839 watcher.waitFor(DESTROYED); 840 841 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 842 assertThat(receivedIds).containsExactly( 843 mainSessionId, 844 childSessionId, 845 grandChildSessionId) 846 .inOrder(); 847 848 // Assert sessions 849 final Session mainTestSession = service.getFinishedSession(mainSessionId); 850 assertMainSessionContext(mainTestSession, activity); 851 assertNoViewLevelEvents(mainTestSession, activity); 852 853 final Session childTestSession = service.getFinishedSession(childSessionId); 854 assertChildSessionContext(childTestSession, "child"); 855 assertThat(childTestSession.getEvents()).isEmpty(); 856 857 final Session grandChildTestSession = service.getFinishedSession(grandChildSessionId); 858 assertChildSessionContext(grandChildTestSession, "grandChild"); 859 assertThat(grandChildTestSession.getEvents()).isEmpty(); 860 861 // Assert lifecycle methods were called in the right order 862 assertLifecycleOrder(1, mainTestSession, CREATION); 863 assertLifecycleOrder(2, childTestSession, CREATION); 864 assertLifecycleOrder(3, grandChildTestSession, CREATION); 865 assertLifecycleOrder(4, grandChildTestSession, DESTRUCTION); 866 assertLifecycleOrder(5, childTestSession, DESTRUCTION); 867 assertLifecycleOrder(6, mainTestSession, DESTRUCTION); 868 } 869 870 /** 871 * Tests scenario where new sessions are added from each other session, but they're not nested 872 * neither have views attached to them. 873 * 874 * <p>This test actions are exactly the same as 875 * {@link #testDinamicallyManageChildlessSiblingSessions()}, except for session nesting (and 876 * order of lifecycle events). 877 */ 878 @Test testDinamicallyManageChildlessNestedSessions()879 public void testDinamicallyManageChildlessNestedSessions() throws Exception { 880 final CtsContentCaptureService service = enableService(); 881 final ActivityWatcher watcher = startWatcher(); 882 883 final ChildlessActivity activity = launchActivity(); 884 watcher.waitFor(RESUMED); 885 886 final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession(); 887 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 888 Log.v(TAG, "main session id: " + mainSessionId); 889 890 // Create 1st session 891 final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1") 892 .build(); 893 final ContentCaptureSession childSession1 = mainSession 894 .createContentCaptureSession(context1); 895 final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId(); 896 Log.v(TAG, "child session id 1: " + childSessionId1); 897 898 // Create 2nd session 899 final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2") 900 .build(); 901 final ContentCaptureSession childSession2 = childSession1 902 .createContentCaptureSession(context2); 903 final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId(); 904 Log.v(TAG, "child session id 2: " + childSessionId2); 905 906 // Close 1st session before opening 3rd 907 childSession1.close(); 908 909 // Create 3nd session... 910 final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3") 911 .build(); 912 final ContentCaptureSession childSession3 = mainSession 913 .createContentCaptureSession(context3); 914 final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId(); 915 Log.v(TAG, "child session id 3: " + childSessionId3); 916 917 // ...and close it right away 918 childSession3.close(); 919 920 // Create 4nd session 921 final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4") 922 .build(); 923 final ContentCaptureSession childSession4 = mainSession 924 .createContentCaptureSession(context4); 925 final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId(); 926 Log.v(TAG, "child session id 4: " + childSessionId4); 927 928 activity.finish(); 929 watcher.waitFor(DESTROYED); 930 931 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 932 assertThat(receivedIds).containsExactly( 933 mainSessionId, 934 childSessionId1, 935 childSessionId2, 936 childSessionId3, 937 childSessionId4) 938 .inOrder(); 939 940 // Assert main sessions info 941 final Session mainTestSession = service.getFinishedSession(mainSessionId); 942 assertMainSessionContext(mainTestSession, activity); 943 assertNoViewLevelEvents(mainTestSession, activity); 944 945 final Session childTestSession1 = service.getFinishedSession(childSessionId1); 946 assertChildSessionContext(childTestSession1, "session1"); 947 assertThat(childTestSession1.getEvents()).isEmpty(); 948 949 final Session childTestSession2 = service.getFinishedSession(childSessionId2); 950 assertChildSessionContext(childTestSession2, "session2"); 951 assertThat(childTestSession2.getEvents()).isEmpty(); 952 953 final Session childTestSession3 = service.getFinishedSession(childSessionId3); 954 assertChildSessionContext(childTestSession3, "session3"); 955 assertThat(childTestSession3.getEvents()).isEmpty(); 956 957 final Session childTestSession4 = service.getFinishedSession(childSessionId4); 958 assertChildSessionContext(childTestSession4, "session4"); 959 assertThat(childTestSession4.getEvents()).isEmpty(); 960 961 // Assert lifecycle methods were called in the right order 962 assertLifecycleOrder(1, mainTestSession, CREATION); 963 assertLifecycleOrder(2, childTestSession1, CREATION); 964 assertLifecycleOrder(3, childTestSession2, CREATION); 965 assertLifecycleOrder(4, childTestSession2, DESTRUCTION); 966 assertLifecycleOrder(5, childTestSession1, DESTRUCTION); 967 assertLifecycleOrder(6, childTestSession3, CREATION); 968 assertLifecycleOrder(7, childTestSession3, DESTRUCTION); 969 assertLifecycleOrder(8, childTestSession4, CREATION); 970 assertLifecycleOrder(9, childTestSession4, DESTRUCTION); 971 assertLifecycleOrder(10, mainTestSession, DESTRUCTION); 972 } 973 974 /** 975 * Tests scenario where views from different session are removed in sequence - they should not 976 * have been batched. 977 */ 978 @Test 979 @RequiresFlagsDisabled(Flags.FLAG_FLUSH_AFTER_EACH_FRAME) testRemoveChildrenFromDifferentSessions_flagOff()980 public void testRemoveChildrenFromDifferentSessions_flagOff() throws Exception { 981 final CtsContentCaptureService service = enableService(); 982 final ActivityWatcher watcher = startWatcher(); 983 984 final ChildlessActivity activity = launchActivity(); 985 watcher.waitFor(RESUMED); 986 final LinearLayout rootView = activity.getRootView(); 987 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 988 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 989 Log.v(TAG, "main session id: " + mainSessionId); 990 991 // Create 1st session 992 final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1") 993 .build(); 994 final ContentCaptureSession childSession1 = mainSession 995 .createContentCaptureSession(context1); 996 final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId(); 997 Log.v(TAG, "child session id 1: " + childSessionId1); 998 999 // Session 1, child 1 1000 final TextView s1c1 = addChild(activity, childSession1, "s1c1"); 1001 final AutofillId s1c1Id = s1c1.getAutofillId(); 1002 Log.v(TAG, "childrens from session1: " + s1c1Id); 1003 1004 // Create 2nd session 1005 final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2") 1006 .build(); 1007 final ContentCaptureSession childSession2 = mainSession 1008 .createContentCaptureSession(context2); 1009 final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId(); 1010 Log.v(TAG, "child session id 2: " + childSessionId2); 1011 1012 final TextView s2c1 = newImportantView(activity, childSession2, "s2c1"); 1013 final AutofillId s2c1Id = s2c1.getAutofillId(); 1014 final TextView s2c2 = newImportantView(activity, childSession2, "s2c2"); 1015 final AutofillId s2c2Id = s2c2.getAutofillId(); 1016 Log.v(TAG, "childrens from session2: " + s2c1Id + ", " + s2c2Id); 1017 1018 // Add 2 children together so they're wrapped a view_tree batch 1019 activity.syncRunOnUiThread(() -> { 1020 rootView.addView(s2c1); 1021 rootView.addView(s2c2); 1022 }); 1023 1024 // Remove views - should generate one batch event for s2 and one single event for s1 1025 waitAndRemoveViews(activity, s2c1, s2c2, s1c1); 1026 1027 activity.finish(); 1028 watcher.waitFor(DESTROYED); 1029 1030 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 1031 assertThat(receivedIds).containsExactly( 1032 mainSessionId, 1033 childSessionId1, 1034 childSessionId2) 1035 .inOrder(); 1036 1037 // Assert main sessions info 1038 final Session mainTestSession = service.getFinishedSession(mainSessionId); 1039 assertMainSessionContext(mainTestSession, activity); 1040 final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents(); 1041 Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents); 1042 1043 // Logs events before asserting 1044 final Session childTestSession1 = service.getFinishedSession(childSessionId1); 1045 assertChildSessionContext(childTestSession1, "session1"); 1046 final List<ContentCaptureEvent> events1 = childTestSession1.getEvents(); 1047 Log.v(TAG, "events1(" + events1.size() + "): " + events1); 1048 final Session childTestSession2 = service.getFinishedSession(childSessionId2); 1049 final List<ContentCaptureEvent> events2 = childTestSession2.getEvents(); 1050 assertChildSessionContext(childTestSession2, "session2"); 1051 Log.v(TAG, "events2(" + events2.size() + "): " + events2); 1052 1053 // Assert children 1054 assertThat(events1.size()).isAtLeast(6); 1055 final AutofillId rootId = rootView.getAutofillId(); 1056 assertViewTreeStarted(events1, 0); 1057 assertViewAppeared(events1, 1, s1c1, rootId); 1058 assertViewTreeFinished(events1, 2); 1059 assertViewTreeStarted(events1, 3); 1060 assertViewDisappeared(events1, 4, s1c1Id); 1061 assertViewTreeFinished(events1, 5); 1062 1063 assertThat(events2.size()).isAtLeast(7); 1064 assertViewTreeStarted(events2, 0); 1065 assertViewAppeared(events2, 1, s2c1, rootId); 1066 assertViewAppeared(events2, 2, s2c2, rootId); 1067 assertViewTreeFinished(events2, 3); 1068 assertViewTreeStarted(events2, 4); 1069 assertViewsDisappeared(events2, 5, s2c1Id, s2c2Id); 1070 assertViewTreeFinished(events2, 6); 1071 } 1072 1073 /** 1074 * Tests scenario where views from different session are removed in sequence - they should not 1075 * have been batched. 1076 */ 1077 @Test 1078 @RequiresFlagsEnabled(Flags.FLAG_FLUSH_AFTER_EACH_FRAME) testRemoveChildrenFromDifferentSessions_flagOn()1079 public void testRemoveChildrenFromDifferentSessions_flagOn() throws Exception { 1080 final CtsContentCaptureService service = enableService(); 1081 final ActivityWatcher watcher = startWatcher(); 1082 1083 final ChildlessActivity activity = launchActivity(); 1084 watcher.waitFor(RESUMED); 1085 final LinearLayout rootView = activity.getRootView(); 1086 final ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 1087 final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId(); 1088 Log.v(TAG, "main session id: " + mainSessionId); 1089 1090 // Create 1st session 1091 final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1") 1092 .build(); 1093 final ContentCaptureSession childSession1 = mainSession 1094 .createContentCaptureSession(context1); 1095 final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId(); 1096 Log.v(TAG, "child session id 1: " + childSessionId1); 1097 1098 // Session 1, child 1 1099 final TextView s1c1 = addChild(activity, childSession1, "s1c1"); 1100 final AutofillId s1c1Id = s1c1.getAutofillId(); 1101 Log.v(TAG, "childrens from session1: " + s1c1Id); 1102 1103 // Create 2nd session 1104 final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2") 1105 .build(); 1106 final ContentCaptureSession childSession2 = mainSession 1107 .createContentCaptureSession(context2); 1108 final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId(); 1109 Log.v(TAG, "child session id 2: " + childSessionId2); 1110 1111 final TextView s2c1 = newImportantView(activity, childSession2, "s2c1"); 1112 final AutofillId s2c1Id = s2c1.getAutofillId(); 1113 final TextView s2c2 = newImportantView(activity, childSession2, "s2c2"); 1114 final AutofillId s2c2Id = s2c2.getAutofillId(); 1115 Log.v(TAG, "childrens from session2: " + s2c1Id + ", " + s2c2Id); 1116 1117 // Add 2 children together so they're wrapped a view_tree batch 1118 activity.syncRunOnUiThread(() -> { 1119 rootView.addView(s2c1); 1120 rootView.addView(s2c2); 1121 }); 1122 1123 // Remove views - should generate one batch event for s2 and one single event for s1 1124 waitAndRemoveViews(activity, s2c1, s2c2, s1c1); 1125 1126 activity.finish(); 1127 watcher.waitFor(DESTROYED); 1128 1129 final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds(); 1130 assertThat(receivedIds).containsExactly( 1131 mainSessionId, 1132 childSessionId1, 1133 childSessionId2) 1134 .inOrder(); 1135 1136 // Assert main sessions info 1137 final Session mainTestSession = service.getFinishedSession(mainSessionId); 1138 assertMainSessionContext(mainTestSession, activity); 1139 final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents(); 1140 Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents); 1141 1142 // Logs events before asserting 1143 final Session childTestSession1 = service.getFinishedSession(childSessionId1); 1144 assertChildSessionContext(childTestSession1, "session1"); 1145 final List<ContentCaptureEvent> events1 = childTestSession1.getEvents(); 1146 Log.v(TAG, "events1(" + events1.size() + "): " + events1); 1147 final Session childTestSession2 = service.getFinishedSession(childSessionId2); 1148 final List<ContentCaptureEvent> events2 = childTestSession2.getEvents(); 1149 assertChildSessionContext(childTestSession2, "session2"); 1150 Log.v(TAG, "events2(" + events2.size() + "): " + events2); 1151 1152 // Assert children 1153 assertThat(events1.size()).isAtLeast(6); 1154 final AutofillId rootId = rootView.getAutofillId(); 1155 assertViewTreeStarted(events1, 0); 1156 assertViewAppeared(events1, 1, s1c1, rootId); 1157 assertViewTreeFinished(events1, 2); 1158 assertSessionFlush(events1, 3); 1159 assertViewTreeStarted(events1, 4); 1160 assertViewDisappeared(events1, 5, s1c1Id); 1161 assertViewTreeFinished(events1, 6); 1162 assertSessionFlush(events1, 7); 1163 1164 assertThat(events2.size()).isAtLeast(7); 1165 assertViewTreeStarted(events2, 0); 1166 assertViewAppeared(events2, 1, s2c1, rootId); 1167 assertViewAppeared(events2, 2, s2c2, rootId); 1168 assertViewTreeFinished(events2, 3); 1169 assertSessionFlush(events2, 4); 1170 assertViewTreeStarted(events2, 5); 1171 assertViewsDisappeared(events2, 6, s2c1Id, s2c2Id); 1172 assertViewTreeFinished(events2, 7); 1173 assertSessionFlush(events2, 8); 1174 } 1175 1176 /* TODO(b/119638528): add more scenarios for nested sessions, such as: 1177 * - add views to the children sessions 1178 * - s1 -> s2 -> s3 and main -> s4; close(s1) then generate events on view from s3 1179 * - s1 -> s2 -> s3 and main -> s4; close(s2) then generate events on view from s3 1180 * - s1 -> s2 and s3->s4 -> s4 1181 * - etc 1182 */ 1183 1184 private enum DisabledReason { 1185 BY_API, 1186 BY_SETTINGS, 1187 BY_DEVICE_CONFIG 1188 } 1189 setFeatureEnabled(@onNull CtsContentCaptureService service, @NonNull DisabledReason reason, boolean enabled)1190 private void setFeatureEnabled(@NonNull CtsContentCaptureService service, 1191 @NonNull DisabledReason reason, 1192 boolean enabled) { 1193 switch (reason) { 1194 case BY_API: 1195 if (enabled) { 1196 // The service cannot re-enable itself, so we use settings instead. 1197 setFeatureEnabledBySettings(true); 1198 } else { 1199 service.disableSelf(); 1200 } 1201 break; 1202 case BY_SETTINGS: 1203 setFeatureEnabledBySettings(enabled); 1204 break; 1205 case BY_DEVICE_CONFIG: 1206 setFeatureEnabledByDeviceConfig(Boolean.toString(enabled)); 1207 break; 1208 default: 1209 throw new IllegalArgumentException("invalid reason: " + reason); 1210 } 1211 } 1212 1213 @Test testIsContentCaptureFeatureEnabled_notService()1214 public void testIsContentCaptureFeatureEnabled_notService() throws Exception { 1215 final ContentCaptureManager mgr = getContentCaptureManagerHack(); 1216 assertThrows(SecurityException.class, () -> mgr.isContentCaptureFeatureEnabled()); 1217 } 1218 1219 @Test testSetContentCaptureFeatureEnabled_disabledBySettings()1220 public void testSetContentCaptureFeatureEnabled_disabledBySettings() throws Exception { 1221 setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_SETTINGS); 1222 } 1223 setContentCaptureFeatureEnabledTest_disabled(@onNull DisabledReason reason)1224 private void setContentCaptureFeatureEnabledTest_disabled(@NonNull DisabledReason reason) 1225 throws Exception { 1226 final ContentCaptureManager mgr = getContentCaptureManagerHack(); 1227 1228 final CtsContentCaptureService service = enableService(); 1229 assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue(); 1230 final DisconnectListener disconnectedListener = service.setOnDisconnectListener(); 1231 1232 setFeatureEnabled(service, reason, /* enabled= */ false); 1233 1234 disconnectedListener.waitForOnDisconnected(); 1235 assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse(); 1236 assertThat(mgr.isContentCaptureEnabled()).isFalse(); 1237 1238 final ActivityWatcher watcher = startWatcher(); 1239 final ChildlessActivity activity = launchActivity(); 1240 1241 watcher.waitFor(RESUMED); 1242 activity.finish(); 1243 watcher.waitFor(DESTROYED); 1244 1245 assertThat(service.getAllSessionIds()).isEmpty(); 1246 } 1247 1248 @Test testSetContentCaptureFeatureEnabled_disabledThenReEnabledBySettings()1249 public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledBySettings() 1250 throws Exception { 1251 setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_SETTINGS); 1252 } 1253 setContentCaptureFeatureEnabledTest_disabledThenReEnabled( @onNull DisabledReason reason)1254 private void setContentCaptureFeatureEnabledTest_disabledThenReEnabled( 1255 @NonNull DisabledReason reason) throws Exception { 1256 final ContentCaptureManager mgr = getContentCaptureManagerHack(); 1257 1258 final CtsContentCaptureService service1 = enableService(); 1259 assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue(); 1260 final DisconnectListener disconnectedListener = service1.setOnDisconnectListener(); 1261 1262 setFeatureEnabled(service1, reason, /* enabled= */ false); 1263 disconnectedListener.waitForOnDisconnected(); 1264 1265 assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse(); 1266 assertThat(mgr.isContentCaptureEnabled()).isFalse(); 1267 1268 // Launch and finish 1st activity while it's disabled 1269 final ActivityWatcher watcher1 = startWatcher(); 1270 final ChildlessActivity activity1 = launchActivity(); 1271 watcher1.waitFor(RESUMED); 1272 activity1.finish(); 1273 watcher1.waitFor(DESTROYED); 1274 1275 // Re-enable feature 1276 CtsContentCaptureService.clearServiceWatcher(); 1277 final ServiceWatcher reconnectionWatcher = CtsContentCaptureService.setServiceWatcher(); 1278 reconnectionWatcher.whitelistSelf(); 1279 setFeatureEnabled(service1, reason, /* enabled= */ true); 1280 final CtsContentCaptureService service2 = reconnectionWatcher.waitOnCreate(); 1281 assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue(); 1282 1283 // Launch and finish 2nd activity while it's enabled 1284 final ActivityLauncher<CustomViewActivity> launcher2 = new ActivityLauncher<>( 1285 sContext, mActivitiesWatcher, CustomViewActivity.class); 1286 final ActivityWatcher watcher2 = launcher2.getWatcher(); 1287 final CustomViewActivity activity2 = launcher2.launchActivity(); 1288 watcher2.waitFor(RESUMED); 1289 activity2.finish(); 1290 watcher2.waitFor(DESTROYED); 1291 1292 assertThat(service1.getAllSessionIds()).isEmpty(); 1293 final Session session = service2.getOnlyFinishedSession(); 1294 activity2.assertDefaultEvents(session); 1295 } 1296 1297 @Test testSetContentCaptureFeatureEnabled_disabledByApi()1298 public void testSetContentCaptureFeatureEnabled_disabledByApi() throws Exception { 1299 setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_API); 1300 } 1301 1302 @Test testSetContentCaptureFeatureEnabled_disabledThenReEnabledByApi()1303 public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByApi() 1304 throws Exception { 1305 setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_API); 1306 } 1307 1308 @Test testSetContentCaptureFeatureEnabled_disabledByDeviceConfig()1309 public void testSetContentCaptureFeatureEnabled_disabledByDeviceConfig() throws Exception { 1310 setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_DEVICE_CONFIG); 1311 // Reset service, otherwise it will reconnect when the deviceConfig value is reset 1312 // on cleanup, which will cause the test to fail 1313 Helper.resetService(); 1314 } 1315 1316 @Test testSetContentCaptureFeatureEnabled_disabledThenReEnabledByDeviceConfig()1317 public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByDeviceConfig() 1318 throws Exception { 1319 setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_DEVICE_CONFIG); 1320 // Reset service, otherwise it will reconnect when the deviceConfig value is reset 1321 // on cleanup, which will cause the test to fail 1322 Helper.resetService(); 1323 } 1324 1325 // TODO(b/123406031): add tests that mix feature_enabled with user_restriction_enabled (and 1326 // make sure mgr.isContentCaptureFeatureEnabled() returns only the state of the 1st) 1327 addChild(@onNull ChildlessActivity activity, @NonNull ContentCaptureSession session, @NonNull String text)1328 private TextView addChild(@NonNull ChildlessActivity activity, 1329 @NonNull ContentCaptureSession session, @NonNull String text) { 1330 final TextView child = newImportantView(activity, text); 1331 child.setContentCaptureSession(session); 1332 Log.i(TAG, "adding " + child.getAutofillId() + " on session " 1333 + session.getContentCaptureSessionId()); 1334 activity.runOnUiThread(() -> activity.getRootView().addView(child)); 1335 return child; 1336 } 1337 1338 // TODO(b/123024698): these method are used in cases where we cannot close a session because we 1339 // would miss intermediate events, so we need to sleep. This is a hack (it's slow and flaky): 1340 // ideally we should block and wait until the service receives the event, but right now 1341 // we don't get the service events until after the activity is finished, so we cannot do that... waitAndClose(@onNull ContentCaptureSession session)1342 private void waitAndClose(@NonNull ContentCaptureSession session) { 1343 Log.d(TAG, "sleeping before closing " + session.getContentCaptureSessionId()); 1344 sleep(); 1345 session.close(); 1346 } 1347 waitAndRemoveViews(@onNull ChildlessActivity activity, @NonNull View... views)1348 private void waitAndRemoveViews(@NonNull ChildlessActivity activity, @NonNull View... views) { 1349 Log.d(TAG, "sleeping before removing " + Arrays.toString(views)); 1350 sleep(); 1351 activity.syncRunOnUiThread(() -> { 1352 for (View view : views) { 1353 activity.getRootView().removeView(view); 1354 } 1355 }); 1356 } 1357 sleep()1358 private void sleep() { 1359 Log.d(TAG, "sleeping for 1s "); 1360 SystemClock.sleep(1_000); 1361 } 1362 1363 // TODO(b/120494182): temporary hack to get the manager, which currently is only available on 1364 // Activity contexts (and would be null from sContext) 1365 @NonNull getContentCaptureManagerHack()1366 private ContentCaptureManager getContentCaptureManagerHack() throws InterruptedException { 1367 final AtomicReference<ContentCaptureManager> ref = new AtomicReference<>(); 1368 LoginActivity.onRootView( 1369 (activity, rootView) -> ref.set(activity.getContentCaptureManager())); 1370 1371 final ActivityLauncher<LoginActivity> launcher = new ActivityLauncher<>( 1372 sContext, mActivitiesWatcher, LoginActivity.class); 1373 final ActivityWatcher watcher = launcher.getWatcher(); 1374 final LoginActivity activity = launcher.launchActivity(); 1375 watcher.waitFor(RESUMED); 1376 activity.finish(); 1377 watcher.waitFor(DESTROYED); 1378 1379 final ContentCaptureManager mgr = ref.get(); 1380 assertThat(mgr).isNotNull(); 1381 1382 return mgr; 1383 } 1384 setFeatureEnabledByDeviceConfig(@ullable String value)1385 private void setFeatureEnabledByDeviceConfig(@Nullable String value) { 1386 Log.d(TAG, "setFeatureEnabledByDeviceConfig(): " + value); 1387 1388 sKillSwitchManager.set(value); 1389 } 1390 1391 @NonNull newContentCaptureContextBuilder(@onNull String id)1392 private ContentCaptureContext.Builder newContentCaptureContextBuilder(@NonNull String id) { 1393 return new ContentCaptureContext.Builder(new LocusId(id)); 1394 } 1395 } 1396