• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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