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