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