1 /*
2  * Copyright (C) 2015 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.ActivityManager.LOCK_TASK_MODE_NONE;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
23 import static android.server.wm.TestTaskOrganizer.INVALID_TASK_ID;
24 import static android.server.wm.WindowManagerState.STATE_RESUMED;
25 import static android.server.wm.WindowManagerState.STATE_STOPPED;
26 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
27 import static android.server.wm.app.Components.NON_RESIZEABLE_ACTIVITY;
28 import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
29 import static android.server.wm.app.Components.SINGLE_INSTANCE_ACTIVITY;
30 import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY;
31 import static android.server.wm.app.Components.TEST_ACTIVITY;
32 import static android.server.wm.app.Components.TEST_ACTIVITY_WITH_SAME_AFFINITY;
33 import static android.server.wm.app.Components.TRANSLUCENT_TEST_ACTIVITY;
34 import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
35 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
36 import static android.server.wm.app27.Components.SDK_27_SEPARATE_PROCESS_ACTIVITY;
37 import static android.server.wm.app27.Components.SDK_27_TEST_ACTIVITY;
38 
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertTrue;
41 import static org.junit.Assert.fail;
42 import static org.junit.Assume.assumeTrue;
43 
44 import android.content.ComponentName;
45 import android.content.res.Resources;
46 import android.platform.test.annotations.Presubmit;
47 import android.server.wm.CommandSession.ActivityCallback;
48 import android.window.WindowContainerToken;
49 import android.window.WindowContainerTransaction;
50 
51 import org.junit.Before;
52 import org.junit.Test;
53 
54 /**
55  * Build/Install/Run:
56  *     atest CtsWindowManagerDeviceTestCases:MultiWindowTests
57  */
58 @Presubmit
59 @android.server.wm.annotation.Group2
60 public class MultiWindowTests extends ActivityManagerTestBase {
61 
62     private boolean mIsHomeRecentsComponent;
63 
64     @Before
65     @Override
setUp()66     public void setUp() throws Exception {
67         super.setUp();
68 
69         mIsHomeRecentsComponent = mWmState.isHomeRecentsComponent();
70 
71         assumeTrue("Skipping test: no split multi-window support",
72                 supportsSplitScreenMultiWindow());
73     }
74 
75     @Test
testMinimumDeviceSize()76     public void testMinimumDeviceSize() {
77         mWmState.assertDeviceDefaultDisplaySizeForMultiWindow(
78                 "Devices supporting multi-window must be larger than the default minimum"
79                         + " task size");
80         mWmState.assertDeviceDefaultDisplaySizeForSplitScreen(
81                 "Devices supporting split-screen multi-window must be larger than the"
82                         + " default minimum display size.");
83     }
84 
85     /** Resizeable activity should be able to enter multi-window mode.*/
86     @Test
testResizeableActivity()87     public void testResizeableActivity() {
88         assertActivitySupportedInSplitScreen(TEST_ACTIVITY);
89     }
90 
91     /**
92      * Depending on the value of
93      * {@link com.android.internal.R.integer.config_supportsNonResizableMultiWindow},
94      * non-resizeable activity may or may not be able to enter multi-window mode.
95      *
96      * Based on the flag value:
97      * -1: not support non-resizable in multi window.
98      *  0: check the screen smallest width, if it is a large screen, support non-resizable in multi
99      *     window. Otherwise, not support.
100      *  1: always support non-resizable in multi window.
101      */
102     @Test
testNonResizeableActivity()103     public void testNonResizeableActivity() {
104         createManagedDevEnableNonResizableMultiWindowSession().set(0);
105         final Resources resources = mContext.getResources();
106         final int configSupportsNonResizableMultiWindow;
107         try {
108             configSupportsNonResizableMultiWindow = resources.getInteger(resources.getIdentifier(
109                     "config_supportsNonResizableMultiWindow", "integer", "android"));
110         } catch (Resources.NotFoundException e) {
111             fail("Device must define config_supportsNonResizableMultiWindow");
112             return;
113         }
114         switch (configSupportsNonResizableMultiWindow) {
115             case -1:
116                 assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
117                 break;
118             case 1:
119                 assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
120                 break;
121             case 0:
122                 final int configLargeScreenSmallestScreenWidthDp;
123                 try {
124                     configLargeScreenSmallestScreenWidthDp =
125                             resources.getInteger(resources.getIdentifier(
126                                     "config_largeScreenSmallestScreenWidthDp",
127                                     "integer", "android"));
128                 } catch (Resources.NotFoundException e) {
129                     fail("Device must define config_largeScreenSmallestScreenWidthDp");
130                     return;
131                 }
132                 final int smallestScreenWidthDp = mWmState.getHomeTask()
133                         .mFullConfiguration.smallestScreenWidthDp;
134                 if (smallestScreenWidthDp >= configLargeScreenSmallestScreenWidthDp) {
135                     assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
136                 } else {
137                     assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
138                 }
139                 break;
140             default:
141                 fail("config_supportsNonResizableMultiWindow must be -1, 0, or 1.");
142         }
143     }
144 
145     /**
146      * Non-resizeable activity can enter split-screen if
147      * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
148      * set.
149      */
150     @Test
testDevEnableNonResizeableMultiWindow_splitScreenPrimary()151     public void testDevEnableNonResizeableMultiWindow_splitScreenPrimary() {
152         createManagedDevEnableNonResizableMultiWindowSession().set(1);
153 
154         assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
155     }
156 
157     /**
158      * Non-resizeable activity can enter split-screen if
159      * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
160      * set.
161      */
162     @Test
testDevEnableNonResizeableMultiWindow_splitScreenSecondary()163     public void testDevEnableNonResizeableMultiWindow_splitScreenSecondary() {
164         createManagedDevEnableNonResizableMultiWindowSession().set(1);
165 
166         launchActivitiesInSplitScreen(
167                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
168                 getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY));
169 
170         mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
171         mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
172         assertTrue(mWmState.containsActivityInWindowingMode(
173                 NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
174     }
175 
176     /** Asserts that the give activity can be shown in split screen. */
assertActivitySupportedInSplitScreen(ComponentName activity)177     private void assertActivitySupportedInSplitScreen(ComponentName activity) {
178         launchActivityInPrimarySplit(activity);
179         mWmState.waitForActivityState(activity, STATE_RESUMED);
180         mWmState.assertVisibility(activity, true);
181         assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_MULTI_WINDOW));
182     }
183 
184     /** Asserts that the give activity can NOT be shown in split screen. */
assertActivityNotSupportedInSplitScreen(ComponentName activity)185     private void assertActivityNotSupportedInSplitScreen(ComponentName activity) {
186         boolean gotAssertionError = false;
187         try {
188             launchActivityInPrimarySplit(activity);
189         } catch (AssertionError e) {
190             gotAssertionError = true;
191         }
192         assertTrue("Trying to put non-resizeable activity in split should throw error.",
193                 gotAssertionError);
194         mWmState.waitForActivityState(activity, STATE_RESUMED);
195         mWmState.assertVisibility(activity, true);
196         assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_FULLSCREEN));
197     }
198 
199     @Test
testLaunchToSideMultiWindowCallbacks()200     public void testLaunchToSideMultiWindowCallbacks() {
201         // Launch two activities in split-screen mode.
202         launchActivitiesInSplitScreen(
203                 getLaunchActivityBuilder().setTargetActivity(NO_RELAUNCH_ACTIVITY),
204                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
205 
206         int displayWindowingMode = mWmState.getDisplay(
207                 mWmState.getDisplayByActivity(TEST_ACTIVITY)).getWindowingMode();
208         separateTestJournal();
209         mTaskOrganizer.dismissSplitScreen();
210         if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
211             // Exit split-screen mode and ensure we only get 1 multi-window mode changed callback.
212             final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
213                     NO_RELAUNCH_ACTIVITY);
214             assertEquals(1,
215                     lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
216         } else {
217             // Display is not a fullscreen display, so there won't be a multi-window callback.
218             // Instead just verify that windows are not in split-screen anymore.
219             waitForIdle();
220             mWmState.computeState();
221             mWmState.assertDoesNotContainStack("Must have exited split-screen",
222                     WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
223         }
224     }
225 
226     @Test
testNoUserLeaveHintOnMultiWindowModeChanged()227     public void testNoUserLeaveHintOnMultiWindowModeChanged() {
228         launchActivity(NO_RELAUNCH_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
229 
230         // Move to primary split.
231         separateTestJournal();
232         putActivityInPrimarySplit(NO_RELAUNCH_ACTIVITY);
233 
234         ActivityLifecycleCounts lifecycleCounts =
235                 waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY);
236         assertEquals("mMultiWindowModeChangedCount",
237                 1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
238         assertEquals("mUserLeaveHintCount",
239                 0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));
240 
241         // Make sure primary split is focused. This way when we dismiss it later fullscreen stack
242         // will come up.
243         launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
244         putActivityInSecondarySplit(LAUNCHING_ACTIVITY);
245 
246         launchActivity(NO_RELAUNCH_ACTIVITY);
247 
248         separateTestJournal();
249 
250         // Move activities back to fullscreen screen.
251         // TestTaskOrganizer sets windowing modes of tasks to unspecific when putting them to split
252         // screens so we need to explicitly set their windowing modes back to fullscreen to avoid
253         // inheriting freeform windowing mode from the display on freeform first devices.
254         int noRelaunchTaskId = mWmState.getTaskByActivity(NO_RELAUNCH_ACTIVITY).mTaskId;
255         WindowContainerToken noRelaunchTaskToken =
256                 mTaskOrganizer.getTaskInfo(noRelaunchTaskId).getToken();
257         WindowContainerTransaction t = new WindowContainerTransaction()
258                 .setWindowingMode(noRelaunchTaskToken, WINDOWING_MODE_FULLSCREEN);
259         mTaskOrganizer.dismissSplitScreen(t, false /* primaryOnTop */);
260 
261         lifecycleCounts = waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY);
262         assertEquals("mMultiWindowModeChangedCount",
263                 1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
264         assertEquals("mUserLeaveHintCount",
265                 0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));
266     }
267 
268     @Test
testLaunchToSideAndBringToFront()269     public void testLaunchToSideAndBringToFront() {
270         launchActivitiesInSplitScreen(
271                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
272                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
273 
274         mWmState.assertFocusedActivity("Launched to side activity must be in front.",
275                 TEST_ACTIVITY);
276 
277         // Launch another activity to side to cover first one.
278         launchActivityInSecondarySplit(NO_RELAUNCH_ACTIVITY);
279         mWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
280                 NO_RELAUNCH_ACTIVITY);
281 
282         // Launch activity that was first launched to side. It should be brought to front.
283         launchActivity(TEST_ACTIVITY);
284         mWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
285                 TEST_ACTIVITY);
286     }
287 
288     @Test
testLaunchToSideMultiple()289     public void testLaunchToSideMultiple() {
290         launchActivitiesInSplitScreen(
291                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
292                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
293 
294         final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount();
295 
296         // Try to launch to side same activity again.
297         launchActivity(TEST_ACTIVITY);
298         mWmState.computeState(TEST_ACTIVITY, LAUNCHING_ACTIVITY);
299         final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount();
300         assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
301         mWmState.assertFocusedActivity("Launched to side activity must remain in front.",
302                 TEST_ACTIVITY);
303     }
304 
305     @Test
testLaunchToSideSingleInstance()306     public void testLaunchToSideSingleInstance() {
307         launchTargetToSide(SINGLE_INSTANCE_ACTIVITY, false);
308     }
309 
310     @Test
testLaunchToSideSingleTask()311     public void testLaunchToSideSingleTask() {
312         launchTargetToSide(SINGLE_TASK_ACTIVITY, false);
313     }
314 
315     @Test
testLaunchToSideMultipleWithDifferentIntent()316     public void testLaunchToSideMultipleWithDifferentIntent() {
317         launchTargetToSide(TEST_ACTIVITY, true);
318     }
319 
launchTargetToSide(ComponentName targetActivityName, boolean taskCountMustIncrement)320     private void launchTargetToSide(ComponentName targetActivityName,
321             boolean taskCountMustIncrement) {
322         launchActivityInPrimarySplit(LAUNCHING_ACTIVITY);
323 
324         // Launch target to side
325         final LaunchActivityBuilder targetActivityLauncher = getLaunchActivityBuilder()
326                 .setTargetActivity(targetActivityName)
327                 .setToSide(true)
328                 .setRandomData(true)
329                 .setMultipleTask(false);
330         targetActivityLauncher.execute();
331         final int secondaryTaskId = mWmState.getTaskByActivity(targetActivityName).mTaskId;
332         mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);
333 
334         mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
335         final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount();
336 
337         // Try to launch to side same activity again with different data.
338         targetActivityLauncher.execute();
339         mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
340 
341         final int[] excludeTaskIds = new int[] { secondaryTaskId, INVALID_TASK_ID };
342         if (taskCountMustIncrement) {
343             mWmState.waitFor("Waiting for new activity to come up.",
344                     state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
345         }
346         WindowManagerState.ActivityTask task = mWmState.getTaskByActivity(targetActivityName,
347                 excludeTaskIds);
348         final int secondaryTaskId2;
349         if (task != null) {
350             secondaryTaskId2 = task.mTaskId;
351             mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId2);
352         } else {
353             secondaryTaskId2 = INVALID_TASK_ID;
354         }
355         final int taskNumberSecondLaunch = mTaskOrganizer.getSecondarySplitTaskCount();
356 
357         if (taskCountMustIncrement) {
358             assertEquals("Task number must be incremented.", taskNumberInitial + 1,
359                     taskNumberSecondLaunch);
360         } else {
361             assertEquals("Task number must not change.", taskNumberInitial,
362                     taskNumberSecondLaunch);
363         }
364         mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
365                 targetActivityName);
366         mWmState.assertFocusedActivity("Launched to side activity must be in front.",
367                 targetActivityName);
368 
369         // Try to launch to side same activity again with different random data. Note that null
370         // cannot be used here, since the first instance of TestActivity is launched with no data
371         // in order to launch into split screen.
372         targetActivityLauncher.execute();
373         mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
374 
375         excludeTaskIds[1] = secondaryTaskId2;
376         if (taskCountMustIncrement) {
377             mWmState.waitFor("Waiting for the second new activity to come up.",
378                     state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
379         }
380         WindowManagerState.ActivityTask taskFinal =
381                 mWmState.getTaskByActivity(targetActivityName, excludeTaskIds);
382         if (taskFinal != null) {
383             int secondaryTaskId3 = taskFinal.mTaskId;
384             mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId3);
385         }
386         final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount();
387 
388         if (taskCountMustIncrement) {
389             assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
390                     taskNumberFinal);
391         } else {
392             assertEquals("Task number must not change.", taskNumberSecondLaunch,
393                     taskNumberFinal);
394         }
395         mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
396                 targetActivityName);
397         mWmState.assertFocusedActivity("Launched to side activity must be in front.",
398                 targetActivityName);
399     }
400 
401     @Test
testLaunchToSideMultipleWithFlag()402     public void testLaunchToSideMultipleWithFlag() {
403         launchActivitiesInSplitScreen(
404                 getLaunchActivityBuilder()
405                         .setTargetActivity(TEST_ACTIVITY),
406                 getLaunchActivityBuilder()
407                         // Try to launch to side same activity again,
408                         // but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
409                         .setMultipleTask(true)
410                         .setTargetActivity(TEST_ACTIVITY));
411         assertTrue("Primary split must contain TEST_ACTIVITY",
412                 mWmState.getRootTask(mTaskOrganizer.getPrimarySplitTaskId())
413                         .containsActivity(TEST_ACTIVITY)
414         );
415 
416         assertTrue("Secondary split must contain TEST_ACTIVITY",
417                 mWmState.getRootTask(mTaskOrganizer.getSecondarySplitTaskId())
418                         .containsActivity(TEST_ACTIVITY)
419                 );
420         mWmState.assertFocusedActivity("Launched to side activity must be in front.",
421                 TEST_ACTIVITY);
422     }
423 
424     @Test
testSameProcessActivityResumedPreQ()425     public void testSameProcessActivityResumedPreQ() {
426         launchActivitiesInSplitScreen(
427                 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
428                 getLaunchActivityBuilder().setTargetActivity(SDK_27_LAUNCHING_ACTIVITY));
429 
430         assertEquals("There must be only one resumed activity in the package.", 1,
431                 mWmState.getResumedActivitiesCountInPackage(
432                         SDK_27_TEST_ACTIVITY.getPackageName()));
433     }
434 
435     @Test
testDifferentProcessActivityResumedPreQ()436     public void testDifferentProcessActivityResumedPreQ() {
437         launchActivitiesInSplitScreen(
438                 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
439                 getLaunchActivityBuilder().setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY));
440 
441         assertEquals("There must be only two resumed activities in the package.", 2,
442                 mWmState.getResumedActivitiesCountInPackage(
443                         SDK_27_TEST_ACTIVITY.getPackageName()));
444     }
445 
446     @Test
testDisallowUpdateWindowingModeWhenInLockedTask()447     public void testDisallowUpdateWindowingModeWhenInLockedTask() {
448         launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
449         final WindowManagerState.ActivityTask task =
450                 mWmState.getStandardRootTaskByWindowingMode(
451                         WINDOWING_MODE_FULLSCREEN).getTopTask();
452 
453         try {
454             // Lock the task
455             runWithShellPermission(() -> mAtm.startSystemLockTaskMode(task.mTaskId));
456             waitForOrFail("Fail to enter locked task mode", () ->
457                     mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE);
458 
459             // Verify specifying non-fullscreen windowing mode will fail.
460             boolean exceptionThrown = false;
461             try {
462                 runWithShellPermission(() -> {
463                     final WindowContainerTransaction wct = new WindowContainerTransaction()
464                             .setWindowingMode(
465                                     mTaskOrganizer.getTaskInfo(task.mTaskId).getToken(),
466                                     WINDOWING_MODE_MULTI_WINDOW);
467                     mTaskOrganizer.applyTransaction(wct);
468                 });
469             } catch (UnsupportedOperationException e) {
470                 exceptionThrown = true;
471             }
472             assertTrue("Not allowed to specify windowing mode while in locked task mode.",
473                     exceptionThrown);
474         } finally {
475             runWithShellPermission(() -> {
476                 mAtm.stopSystemLockTaskMode();
477             });
478         }
479     }
480 
481     @Test
testDisallowHierarchyOperationWhenInLockedTask()482     public void testDisallowHierarchyOperationWhenInLockedTask() {
483         launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
484         launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW);
485         final WindowManagerState.ActivityTask task = mWmState
486                 .getStandardRootTaskByWindowingMode(WINDOWING_MODE_FULLSCREEN).getTopTask();
487         final WindowManagerState.ActivityTask root = mWmState
488                 .getStandardRootTaskByWindowingMode(WINDOWING_MODE_MULTI_WINDOW).getTopTask();
489 
490         try {
491             // Lock the task
492             runWithShellPermission(() -> {
493                 mAtm.startSystemLockTaskMode(task.mTaskId);
494             });
495             waitForOrFail("Fail to enter locked task mode", () ->
496                     mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE);
497 
498             boolean gotAssertionError = false;
499             try {
500                 runWithShellPermission(() -> {
501                     // Fetch tokens of testing task and multi-window root.
502                     final WindowContainerToken multiWindowRoot =
503                             mTaskOrganizer.getTaskInfo(root.mTaskId).getToken();
504                     final WindowContainerToken testChild =
505                             mTaskOrganizer.getTaskInfo(task.mTaskId).getToken();
506 
507                     // Verify performing reparent operation is no operation.
508                     final WindowContainerTransaction wct = new WindowContainerTransaction()
509                             .reparent(testChild, multiWindowRoot, true /* onTop */);
510                     mTaskOrganizer.applyTransaction(wct);
511                     waitForOrFail("Fail to reparent", () ->
512                             mTaskOrganizer.getTaskInfo(task.mTaskId).getParentTaskId()
513                                     == root.mTaskId);
514                 });
515             } catch (AssertionError e) {
516                 gotAssertionError = true;
517             }
518             assertTrue("Not allowed to perform hierarchy operation while in locked task mode.",
519                     gotAssertionError);
520         } finally {
521             runWithShellPermission(() -> {
522                 mAtm.stopSystemLockTaskMode();
523             });
524         }
525     }
526 
527     /**
528      * Asserts that the activity is visible when the top opaque activity finishes and with another
529      * translucent activity on top while in split-screen-secondary task.
530      */
531     @Test
testVisibilityWithTranslucentAndTopFinishingActivity()532     public void testVisibilityWithTranslucentAndTopFinishingActivity() {
533         // Launch two activities in split-screen mode.
534         launchActivitiesInSplitScreen(
535                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
536                 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY));
537 
538         mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
539 
540         // Launch two more activities on a different task on top of split-screen-secondary and
541         // only the top opaque activity should be visible.
542         // Explicitly launch them into fullscreen mode because the control windowing mode of the
543         // launch root doesn't include freeform mode. Freeform first devices launch apps in freeform
544         // mode by default, which won't trigger the launch root.
545         getLaunchActivityBuilder().setTargetActivity(TRANSLUCENT_TEST_ACTIVITY)
546                 .setUseInstrumentation()
547                 .setWaitForLaunched(true)
548                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
549                 .execute();
550         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
551                 .setUseInstrumentation()
552                 .setWaitForLaunched(true)
553                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
554                 .execute();
555         mWmState.assertVisibility(TEST_ACTIVITY, true);
556         mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_STOPPED);
557         mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, false);
558         mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, false);
559 
560         // Finish the top opaque activity and both the two activities should be visible.
561         mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
562         mWmState.computeState(new WaitForValidActivityState(TRANSLUCENT_TEST_ACTIVITY));
563         mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, true);
564         mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, true);
565     }
566 }
567