1 /*
2  * Copyright (C) 2021 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.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
21 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
22 import static android.server.wm.SplitActivityLifecycleTest.ActivityB.EXTRA_SHOW_WHEN_LOCKED;
23 import static android.server.wm.WindowManagerState.STATE_STARTED;
24 import static android.server.wm.WindowManagerState.STATE_STOPPED;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 import static com.google.common.truth.Truth.assertWithMessage;
28 
29 import static org.junit.Assume.assumeTrue;
30 
31 import android.app.Activity;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.graphics.Rect;
36 import android.os.Bundle;
37 import android.os.IBinder;
38 import android.platform.test.annotations.Presubmit;
39 import android.server.wm.WindowManagerState.Task;
40 import android.server.wm.WindowManagerState.TaskFragment;
41 import android.window.TaskFragmentCreationParams;
42 import android.window.TaskFragmentInfo;
43 import android.window.WindowContainerToken;
44 import android.window.WindowContainerTransaction;
45 
46 import org.junit.Ignore;
47 import org.junit.Test;
48 
49 /**
50  * Tests that verify the behavior of split Activity.
51  * <p>
52  * At the beginning of test, two Activities are launched side-by-side in two adjacent TaskFragments.
53  * Then another Activity will be launched with different scenarios. The purpose of this test is to
54  * verify the CUJ of split Activity.
55  * </p>
56  *
57  * Build/Install/Run:
58  *     atest CtsWindowManagerDeviceTestCases:SplitActivityLifecycleTest
59  */
60 @Presubmit
61 public class SplitActivityLifecycleTest extends TaskFragmentOrganizerTestBase {
62     private Activity mOwnerActivity;
63     private IBinder mOwnerToken;
64     private final Rect mPrimaryBounds = new Rect();
65     private final Rect mSideBounds = new Rect();
66     private TaskFragmentRecord mTaskFragA;
67     private TaskFragmentRecord mTaskFragB;
68     private final ComponentName mActivityA = new ComponentName(mContext, ActivityA.class);
69     private final ComponentName mActivityB = new ComponentName(mContext, ActivityB.class);
70     private final ComponentName mActivityC = new ComponentName(mContext, ActivityC.class);
71     private final Intent mIntent = new Intent().setComponent(mActivityC);
72 
73     @Override
setUp()74     public void setUp() throws Exception {
75         super.setUp();
76         mOwnerActivity = startActivity(ActivityA.class);
77         mOwnerToken = getActivityToken(mOwnerActivity);
78     }
79 
80     /** Launch two Activities in two adjacent TaskFragments side-by-side. */
initializeSplitActivities(boolean splitInEmbeddedTask)81     private void initializeSplitActivities(boolean splitInEmbeddedTask) {
82         initializeSplitActivities(splitInEmbeddedTask, false /* showWhenLocked */);
83     }
84 
85     /**
86      * Launch two Activities in two adjacent TaskFragments side-by-side and support to set the
87      * showWhenLocked attribute to Activity B.
88      */
initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked)89     private void initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked) {
90         final Rect activityBounds = mOwnerActivity.getWindowManager().getCurrentWindowMetrics()
91                 .getBounds();
92         activityBounds.splitVertically(mPrimaryBounds, mSideBounds);
93 
94         final TaskFragmentCreationParams paramsA = generatePrimaryTaskFragParams();
95         final TaskFragmentCreationParams paramsB = generateSideTaskFragParams();
96         IBinder taskFragTokenA = paramsA.getFragmentToken();
97         IBinder taskFragTokenB = paramsB.getFragmentToken();
98 
99         final WindowContainerTransaction wct = new WindowContainerTransaction()
100                 .createTaskFragment(paramsA)
101                 .reparentActivityToTaskFragment(taskFragTokenA, mOwnerToken)
102                 .createTaskFragment(paramsB)
103                 .setAdjacentTaskFragments(taskFragTokenA, taskFragTokenB, null /* params */);
104 
105         final Intent intent = new Intent().setComponent(mActivityB);
106         if (splitInEmbeddedTask) {
107             intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
108         }
109         if (showWhenLocked) {
110             intent.putExtra(EXTRA_SHOW_WHEN_LOCKED, true);
111         }
112         wct.startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent,
113                 null /* activityOptions */);
114 
115         mTaskFragmentOrganizer.setAppearedCount(2);
116         mTaskFragmentOrganizer.applyTransaction(wct);
117         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
118 
119         final TaskFragmentInfo infoA = mTaskFragmentOrganizer.getTaskFragmentInfo(
120                 taskFragTokenA);
121         final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo(
122                 taskFragTokenB);
123 
124         assertNotEmptyTaskFragment(infoA, taskFragTokenA, mOwnerToken);
125         assertNotEmptyTaskFragment(infoB, taskFragTokenB);
126 
127         mTaskFragA = new TaskFragmentRecord(infoA);
128         mTaskFragB = new TaskFragmentRecord(infoB);
129 
130         waitAndAssertResumedActivity(mActivityA, "Activity A must still be resumed.");
131         waitAndAssertResumedActivity(mActivityB, "Activity B must still be resumed.");
132 
133         if (splitInEmbeddedTask) {
134             TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB);
135             Task embeddedTask = mWmState.getTaskByActivity(mActivityB);
136             assertWindowHierarchy(taskFragmentB, embeddedTask, mWmState.getActivity(mActivityB));
137         }
138 
139         mTaskFragmentOrganizer.resetLatch();
140     }
141 
142     /**
143      * Verifies the behavior to launch Activity in the same TaskFragment as the owner Activity.
144      * <p>
145      * For example, given that Activity A and B are showed side-by-side, this test verifies
146      * the behavior to launch Activity C in the same TaskFragment as Activity A:
147      * <pre class="prettyprint">
148      * |A|B| -> |C|B|
149      * </pre></p>
150      */
151     @Test
testActivityLaunchInSameSplitTaskFragment()152     public void testActivityLaunchInSameSplitTaskFragment() {
153         // Initialize test environment by launching Activity A and B side-by-side.
154         initializeSplitActivities(false /* verifyEmbeddedTask */);
155 
156         final IBinder taskFragTokenA = mTaskFragA.getTaskFragToken();
157         final WindowContainerTransaction wct = new WindowContainerTransaction()
158                 .startActivityInTaskFragment(taskFragTokenA, mOwnerToken, mIntent,
159                         null /* activityOptions */);
160 
161         mTaskFragmentOrganizer.applyTransaction(wct);
162 
163         final TaskFragmentInfo infoA = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo(
164                 taskFragTokenA, info -> info.getActivities().size() == 2,
165                 "getActivities from TaskFragment A must contain 2 activities");
166 
167         assertNotEmptyTaskFragment(infoA, taskFragTokenA, mOwnerToken);
168 
169         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
170         waitAndAssertActivityState(mActivityA, STATE_STOPPED,
171                 "Activity A is occluded by Activity C, so it must be stopped.");
172         waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
173 
174         final TaskFragment taskFragmentA = mWmState.getTaskFragmentByActivity(mActivityA);
175         assertWithMessage("TaskFragmentA must contain Activity A and C")
176                 .that(taskFragmentA.mActivities).containsExactly(mWmState.getActivity(mActivityA),
177                 mWmState.getActivity(mActivityC));
178     }
179 
180     /**
181      * Verifies the behavior to launch Activity in the adjacent TaskFragment.
182      * <p>
183      * For example, given that Activity A and B are showed side-by-side, this test verifies
184      * the behavior to launch Activity C in the same TaskFragment as Activity B:
185      * <pre class="prettyprint">
186      * |A|B| -> |A|C|
187      * </pre></p>
188      */
189     @Test
testActivityLaunchInAdjacentSplitTaskFragment()190     public void testActivityLaunchInAdjacentSplitTaskFragment() {
191         // Initialize test environment by launching Activity A and B side-by-side.
192         initializeSplitActivities(false /* verifyEmbeddedTask */);
193 
194         final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken();
195         final WindowContainerTransaction wct = new WindowContainerTransaction()
196                 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, mIntent,
197                         null /* activityOptions */);
198 
199         mTaskFragmentOrganizer.applyTransaction(wct);
200 
201         final TaskFragmentInfo infoB = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo(
202                 taskFragTokenB, info -> info.getActivities().size() == 2,
203                 "getActivities from TaskFragment A must contain 2 activities");
204 
205         assertNotEmptyTaskFragment(infoB, taskFragTokenB);
206 
207         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
208         waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
209         waitAndAssertActivityState(mActivityB, STATE_STOPPED,
210                 "Activity B is occluded by Activity C, so it must be stopped.");
211 
212         final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB);
213         assertWithMessage("TaskFragmentB must contain Activity B and C")
214                 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityB),
215                 mWmState.getActivity(mActivityC));
216     }
217 
218     /**
219      * Verifies the behavior that the Activity instance in bottom TaskFragment calls
220      * {@link Context#startActivity(Intent)} to launch another Activity.
221      * <p>
222      * For example, given that Activity A and B are showed side-by-side, Activity A calls
223      * {@link Context#startActivity(Intent)} to launch Activity C. The expected behavior is that
224      * Activity C will be launch on top of Activity B as below:
225      * <pre class="prettyprint">
226      * |A|B| -> |A|C|
227      * </pre>
228      * The reason is that TaskFragment B has higher z-order than TaskFragment A because we create
229      * TaskFragment B later than TaskFragment A.
230      * </p>
231      */
232     @Test
testActivityLaunchFromBottomTaskFragment()233     public void testActivityLaunchFromBottomTaskFragment() {
234         // Initialize test environment by launching Activity A and B side-by-side.
235         initializeSplitActivities(false /* verifyEmbeddedTask */);
236 
237         mOwnerActivity.startActivity(mIntent);
238 
239         final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken();
240         final TaskFragmentInfo infoB = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo(
241                 taskFragTokenB, info -> info.getActivities().size() == 2,
242                 "getActivities from TaskFragment A must contain 2 activities");
243 
244         assertNotEmptyTaskFragment(infoB, taskFragTokenB);
245 
246         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
247         waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
248         waitAndAssertActivityState(mActivityB, STATE_STOPPED,
249                 "Activity B is occluded by Activity C, so it must be stopped.");
250 
251         final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB);
252         assertWithMessage("TaskFragmentB must contain Activity B and C")
253                 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityB),
254                 mWmState.getActivity(mActivityC));
255     }
256 
257     /**
258      * Verifies the behavior of the activities in a TaskFragment that is sandwiched in adjacent
259      * TaskFragments.
260      */
261     @Test
testSandwichTaskFragmentInAdjacent()262     public void testSandwichTaskFragmentInAdjacent() {
263         // Initialize test environment by launching Activity A and B side-by-side.
264         initializeSplitActivities(false /* verifyEmbeddedTask */);
265 
266         final IBinder taskFragTokenA = mTaskFragA.getTaskFragToken();
267         final TaskFragmentCreationParams paramsC = generateSideTaskFragParams();
268         final IBinder taskFragTokenC = paramsC.getFragmentToken();
269         final WindowContainerTransaction wct = new WindowContainerTransaction()
270                 // Create the side TaskFragment for C and launch
271                 .createTaskFragment(paramsC)
272                 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent,
273                         null /* activityOptions */)
274                 .setAdjacentTaskFragments(taskFragTokenA, taskFragTokenC, null /* options */);
275 
276         mTaskFragmentOrganizer.applyTransaction(wct);
277         // Wait for the TaskFragment of Activity C to be created.
278         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
279 
280         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
281         waitAndAssertActivityState(mActivityB, STATE_STOPPED,
282                 "Activity B is occluded by Activity C, so it must be stopped.");
283         waitAndAssertResumedActivity(mActivityA, "Activity B must be resumed.");
284     }
285 
286     /**
287      * Verifies the behavior to launch adjacent Activity to the adjacent TaskFragment.
288      * <p>
289      * For example, given that Activity A and B are showed side-by-side, this test verifies
290      * the behavior to launch the Activity C to the adjacent TaskFragment of the secondary
291      * TaskFragment, which Activity B is attached to. Then the secondary TaskFragment is shifted to
292      * occlude the primary TaskFragment, which Activity A is attached to, and the adjacent
293      * TaskFragment, which Activity C is attached to, is occupied the region where the secondary
294      * TaskFragment is located. This test is to verify the "shopping mode" scenario.
295      * <pre class="prettyprint">
296      * |A|B| -> |B|C|
297      * </pre></p>
298      */
299     @Test
testAdjacentActivityLaunchFromSecondarySplitTaskFragment()300     public void testAdjacentActivityLaunchFromSecondarySplitTaskFragment() {
301         // Initialize test environment by launching Activity A and B side-by-side.
302         initializeSplitActivities(false /* verifyEmbeddedTask */);
303 
304         final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken();
305         final TaskFragmentCreationParams paramsC = generateSideTaskFragParams();
306         final IBinder taskFragTokenC = paramsC.getFragmentToken();
307         final WindowContainerTransaction wct = new WindowContainerTransaction()
308                 // Move TaskFragment B to the primaryBounds
309                 .setBounds(mTaskFragB.getToken(), mPrimaryBounds)
310                 // Create the side TaskFragment for C and launch
311                 .createTaskFragment(paramsC)
312                 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent,
313                         null /* activityOptions */)
314                 .setAdjacentTaskFragments(taskFragTokenB, taskFragTokenC, null /* options */);
315 
316         mTaskFragmentOrganizer.applyTransaction(wct);
317         // Wait for the TaskFragment of Activity C to be created.
318         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
319         // Wait for the TaskFragment of Activity B to be changed.
320         mTaskFragmentOrganizer.waitForTaskFragmentInfoChanged();
321 
322         final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenB);
323         final TaskFragmentInfo infoC = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenC);
324 
325         assertNotEmptyTaskFragment(infoB, taskFragTokenB);
326         assertNotEmptyTaskFragment(infoC, taskFragTokenC);
327 
328         mTaskFragB = new TaskFragmentRecord(infoB);
329         final TaskFragmentRecord taskFragC = new TaskFragmentRecord(infoC);
330 
331         assertThat(mTaskFragB.getBounds()).isEqualTo(mPrimaryBounds);
332         assertThat(taskFragC.getBounds()).isEqualTo(mSideBounds);
333 
334         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
335         waitAndAssertActivityState(mActivityA, STATE_STOPPED,
336                 "Activity A is occluded by Activity C, so it must be stopped.");
337         waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
338     }
339 
340     /**
341      * Verifies the behavior to launch Activity in expanded TaskFragment.
342      * <p>
343      * For example, given that Activity A and B are showed side-by-side, this test verifies
344      * the behavior to launch Activity C in the TaskFragment which fills the Task bounds of owner
345      * Activity:
346      * <pre class="prettyprint">
347      * |A|B| -> |C|
348      * </pre></p>
349      */
350     @Test
testActivityLaunchInExpandedTaskFragment()351     public void testActivityLaunchInExpandedTaskFragment() {
352         // Initialize test environment by launching Activity A and B side-by-side.
353         initializeSplitActivities(false /* verifyEmbeddedTask */);
354 
355         testActivityLaunchInExpandedTaskFragmentInternal();
356     }
357 
358     /**
359      * Verifies the behavior to launch Activity in expanded TaskFragment and occludes the embedded
360      * Task.
361      * <p>
362      * For example, given that Activity A and B are showed side-by-side, which Activity B is in
363      * embedded Task, this test verifies the behavior to launch Activity C in the TaskFragment which
364      * fills the Task bounds of owner Activity:
365      * <pre class="prettyprint">
366      *     - Fullscreen -
367      *     TaskFragmentC
368      *       - ActivityC <---- new started Activity
369      * - Left -      - Right -
370      * TaskFragmentA TaskFragmentB
371      *   - ActivityA   - Embedded Task
372      *                   - ActivityB
373      * </pre></p>
374      */
375     @Test
376     @Ignore("b/197364677")
testActivityLaunchInExpandedTaskFragment_AboveEmbeddedTask()377     public void testActivityLaunchInExpandedTaskFragment_AboveEmbeddedTask() {
378         // Initialize test environment by launching Activity A and B side-by-side.
379         initializeSplitActivities(true /* verifyEmbeddedTask */);
380 
381         testActivityLaunchInExpandedTaskFragmentInternal();
382     }
383 
testActivityLaunchInExpandedTaskFragmentInternal()384     private void testActivityLaunchInExpandedTaskFragmentInternal() {
385 
386         final TaskFragmentCreationParams fullScreenParamsC = mTaskFragmentOrganizer
387                 .generateTaskFragParams(mOwnerToken);
388         final IBinder taskFragTokenC = fullScreenParamsC.getFragmentToken();
389         final WindowContainerTransaction wct = new WindowContainerTransaction()
390                 .createTaskFragment(fullScreenParamsC)
391                 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent,
392                         null /* activityOptions */);
393 
394         mTaskFragmentOrganizer.applyTransaction(wct);
395 
396         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
397 
398         assertNotEmptyTaskFragment(mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenC),
399                 taskFragTokenC);
400 
401         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
402         waitAndAssertActivityState(mActivityA, STATE_STOPPED,
403                 "Activity A is occluded by Activity C, so it must be stopped.");
404         waitAndAssertActivityState(mActivityB, STATE_STOPPED,
405                 "Activity B is occluded by Activity C, so it must be stopped.");
406     }
407 
408     /**
409      * Verifies the behavior to launch Activity above the embedded Task in TaskFragment.
410      * <p>
411      * For example, given that Activity A and B are showed side-by-side, which Activity B is in
412      * embedded Task, this test verifies the behavior to launch Activity C on top of the embedded
413      * Task in the same TaskFragment as Activity B:
414      * <pre class="prettyprint">
415      * - Left -      - Right -
416      * TaskFragmentA TaskFragmentB
417      *   - ActivityA   - ActivityC <---- new started Activity
418      *                 - Embedded Task
419      *                   - ActivityB
420      * </pre></p>
421      */
422     @Test
423     @Ignore("b/197364677")
testActivityLaunchAboveEmbeddedTaskInTaskFragment()424     public void testActivityLaunchAboveEmbeddedTaskInTaskFragment() {
425         // Initialize test environment by launching Activity A and B side-by-side.
426         initializeSplitActivities(true /* verifyEmbeddedTask */);
427 
428         final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken();
429 
430         WindowContainerTransaction wct = new WindowContainerTransaction()
431                 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, mIntent,
432                         null /* activityOptions */);
433 
434         mTaskFragmentOrganizer.applyTransaction(wct);
435 
436         mTaskFragmentOrganizer.waitForTaskFragmentInfoChanged();
437 
438         final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenB);
439 
440         assertNotEmptyTaskFragment(infoB, taskFragTokenB);
441 
442         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
443         waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
444         waitAndAssertActivityState(mActivityB, WindowManagerState.STATE_STOPPED,
445                 "Activity B is occluded by Activity C, so it must be stopped.");
446 
447         final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB);
448         assertWithMessage("TaskFragmentB must contain Activity C")
449                 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityC));
450     }
451 
452     /**
453      * Verifies the behavior to launch Activity to the embedded Task in TaskFragment.
454      * <p>
455      * For example, given that Activity A and B are showed side-by-side, which Activity B is in
456      * embedded Task, this test verifies the behavior to launch Activity C to the embedded Task
457      * and on top of Activity B:
458      * <pre class="prettyprint">
459      * - Left -      - Right -
460      * TaskFragmentA TaskFragmentB
461      *   - ActivityA   - Embedded Task
462      *                   - ActivityC <---- new started Activity
463      *                   - ActivityB
464      * </pre></p>
465      */
466     @Test
467     @Ignore("b/197364677")
testActivityLaunchToEmbeddedTaskInTaskFragment()468     public void testActivityLaunchToEmbeddedTaskInTaskFragment() {
469         // Initialize test environment by launching Activity A and B side-by-side.
470         initializeSplitActivities(true /* verifyEmbeddedTask */);
471 
472         final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken();
473         // Make Activity C launch to the embedded Task.
474         final Intent intent = new Intent(mIntent).addFlags(FLAG_ACTIVITY_NEW_TASK);
475 
476         WindowContainerTransaction wct = new WindowContainerTransaction()
477                 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent,
478                         null /* activityOptions */);
479 
480         mTaskFragmentOrganizer.applyTransaction(wct);
481 
482         waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
483         waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
484         waitAndAssertActivityState(mActivityB, STATE_STOPPED,
485                 "Activity B is occluded by Activity C, so it must be stopped.");
486 
487         final Task embeddedTask = mWmState.getTaskByActivity(mActivityB);
488         assertWithMessage("Embedded Task must contain Activity B and Activity C")
489                 .that(embeddedTask.mActivities).containsExactly(mWmState.getActivity(mActivityB),
490                 mWmState.getActivity(mActivityC));
491     }
492 
493     /**
494      * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
495      * embedded activities even if one of Activity has showWhenLocked flag.
496      */
497     @Test
testLaunchEmbeddedActivityWithShowWhenLocked()498     public void testLaunchEmbeddedActivityWithShowWhenLocked() {
499         assumeTrue(supportsLockScreen());
500 
501         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
502         // Initialize test environment by launching Activity A and B (with showWhenLocked)
503         // side-by-side.
504         initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
505 
506         lockScreenSession.sleepDevice();
507         lockScreenSession.wakeUpDevice();
508 
509         waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
510         waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
511     }
512 
513     /**
514      * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
515      * embedded activities if the activities don't have showWhenLocked flag.
516      */
517     @Test
testLaunchEmbeddedActivitiesWithoutShowWhenLocked()518     public void testLaunchEmbeddedActivitiesWithoutShowWhenLocked() {
519         assumeTrue(supportsLockScreen());
520 
521         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
522         // Initialize test environment by launching Activity A and B side-by-side.
523         initializeSplitActivities(false /* verifyEmbeddedTask */, false /* showWhenLocked */);
524 
525         lockScreenSession.sleepDevice();
526         lockScreenSession.wakeUpDevice();
527 
528         waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
529         waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
530     }
531 
532     /**
533      * Verifies the show-when-locked behavior while launch embedded activities. The embedded
534      * activities should be shown on top of the lock screen since they have the showWhenLocked flag.
535      * Don't show the embedded activities even if one of Activity has showWhenLocked flag.
536      */
537     @Test
testLaunchEmbeddedActivitiesWithShowWhenLocked()538     public void testLaunchEmbeddedActivitiesWithShowWhenLocked() {
539         assumeTrue(supportsLockScreen());
540 
541         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
542         // Initialize test environment by launching Activity A and B side-by-side.
543         mOwnerActivity.setShowWhenLocked(true);
544         initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
545 
546         lockScreenSession.sleepDevice();
547         lockScreenSession.wakeUpDevice();
548 
549         waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
550         waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
551 
552         // Launch Activity C without show-when-lock and verifies that both activities are stopped.
553         mOwnerActivity.startActivity(mIntent);
554         waitAndAssertActivityState(mActivityA, STATE_STOPPED, "Activity A must be stopped");
555         waitAndAssertActivityState(mActivityC, STATE_STOPPED, "Activity C must be stopped");
556     }
557 
558     /**
559      * Verifies an Activity below adjacent translucent TaskFragments is visible.
560      */
561     @Test
testTranslucentAdjacentTaskFragment()562     public void testTranslucentAdjacentTaskFragment() {
563         // Create ActivityB on top of ActivityA
564         Activity activityB = startActivity(ActivityB.class);
565         waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
566         waitAndAssertActivityState(mActivityA, STATE_STOPPED,
567                 "Activity A is occluded by Activity B, so it must be stopped.");
568 
569         // Create two adjacent TaskFragments, making ActivityB and TranslucentActivity
570         // displayed side-by-side (ActivityB|TranslucentActivity).
571         mOwnerActivity.getWindowManager().getCurrentWindowMetrics().getBounds()
572                 .splitVertically(mPrimaryBounds, mSideBounds);
573         final TaskFragmentCreationParams primaryParams = generatePrimaryTaskFragParams();
574         final TaskFragmentCreationParams secondaryParams = generateSideTaskFragParams();
575         IBinder primaryToken = primaryParams.getFragmentToken();
576         IBinder secondaryToken = secondaryParams.getFragmentToken();
577 
578         final ComponentName translucentActivity = new ComponentName(mContext,
579                 TranslucentActivity.class);
580         final Intent intent = new Intent().setComponent(translucentActivity);
581         WindowContainerTransaction wct = new WindowContainerTransaction()
582                 .createTaskFragment(primaryParams)
583                 .reparentActivityToTaskFragment(primaryToken, getActivityToken(activityB))
584                 .createTaskFragment(secondaryParams)
585                 .setAdjacentTaskFragments(primaryToken, secondaryToken, null /* params */)
586                 .startActivityInTaskFragment(secondaryToken, mOwnerToken, intent,
587                         null /* activityOptions */);
588         mTaskFragmentOrganizer.applyTransaction(wct);
589 
590         waitAndAssertResumedActivity(translucentActivity, "TranslucentActivity must be resumed.");
591         waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
592         waitAndAssertActivityState(mActivityA, STATE_STARTED,
593                 "Activity A is not fully occluded and must be visible and started");
594     }
595 
generatePrimaryTaskFragParams()596     private TaskFragmentCreationParams generatePrimaryTaskFragParams() {
597         return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mPrimaryBounds,
598                 WINDOWING_MODE_MULTI_WINDOW);
599     }
600 
generateSideTaskFragParams()601     private TaskFragmentCreationParams generateSideTaskFragParams() {
602         return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mSideBounds,
603                 WINDOWING_MODE_MULTI_WINDOW);
604     }
605 
606     private static class TaskFragmentRecord {
607         private final IBinder mTaskFragToken;
608         private final Rect mBounds = new Rect();
609         private final WindowContainerToken mContainerToken;
610 
TaskFragmentRecord(TaskFragmentInfo info)611         private TaskFragmentRecord(TaskFragmentInfo info) {
612             mTaskFragToken = info.getFragmentToken();
613             mBounds.set(info.getConfiguration().windowConfiguration.getBounds());
614             mContainerToken = info.getToken();
615         }
616 
getTaskFragToken()617         private IBinder getTaskFragToken() {
618             return mTaskFragToken;
619         }
620 
getBounds()621         private Rect getBounds() {
622             return mBounds;
623         }
624 
getToken()625         private WindowContainerToken getToken() {
626             return mContainerToken;
627         }
628     }
629 
630     public static class ActivityA extends SplitTestActivity {}
631     public static class ActivityB extends SplitTestActivity {}
632     public static class ActivityC extends SplitTestActivity {}
633     public static class TranslucentActivity extends SplitTestActivity {}
634     public static class SplitTestActivity extends FocusableActivity {
635         public static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked";
636         @Override
onCreate(Bundle icicle)637         protected void onCreate(Bundle icicle) {
638             super.onCreate(icicle);
639             if (getIntent().getBooleanExtra(EXTRA_SHOW_WHEN_LOCKED, false)) {
640                 setShowWhenLocked(true);
641             }
642         }
643     }
644 }
645