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