1 /*
2  * Copyright (C) 2024 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.multidisplay;
18 
19 import static android.server.wm.ComponentNameUtils.getActivityName;
20 import static android.server.wm.ShellCommandHelper.executeShellCommand;
21 import static android.server.wm.WindowManagerState.STATE_RESUMED;
22 import static android.server.wm.app.Components.DISPLAY_ACCESS_CHECK_EMBEDDING_ACTIVITY;
23 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
24 import static android.server.wm.app.Components.LAUNCH_BROADCAST_RECEIVER;
25 import static android.server.wm.app.Components.LaunchBroadcastReceiver.ACTION_TEST_ACTIVITY_START;
26 import static android.server.wm.app.Components.LaunchBroadcastReceiver.EXTRA_COMPONENT_NAME;
27 import static android.server.wm.app.Components.LaunchBroadcastReceiver.EXTRA_TARGET_DISPLAY;
28 import static android.server.wm.app.Components.LaunchBroadcastReceiver.LAUNCH_BROADCAST_ACTION;
29 import static android.server.wm.app.Components.TEST_ACTIVITY;
30 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
31 import static android.server.wm.second.Components.EMBEDDING_ACTIVITY;
32 import static android.server.wm.second.Components.EmbeddingActivity.ACTION_EMBEDDING_TEST_ACTIVITY_START;
33 import static android.server.wm.second.Components.EmbeddingActivity.EXTRA_EMBEDDING_COMPONENT_NAME;
34 import static android.server.wm.second.Components.EmbeddingActivity.EXTRA_EMBEDDING_TARGET_DISPLAY;
35 import static android.server.wm.second.Components.SECOND_ACTIVITY;
36 import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_ACTION;
37 import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER;
38 import static android.server.wm.second.Components.SECOND_NO_EMBEDDING_ACTIVITY;
39 import static android.server.wm.second.Components.SecondActivity.EXTRA_DISPLAY_ACCESS_CHECK;
40 import static android.server.wm.third.Components.THIRD_ACTIVITY;
41 import static android.view.Display.DEFAULT_DISPLAY;
42 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
43 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
44 import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
45 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
46 
47 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
48 
49 import static org.junit.Assert.assertEquals;
50 import static org.junit.Assert.assertFalse;
51 import static org.junit.Assert.assertNull;
52 import static org.junit.Assert.assertTrue;
53 import static org.junit.Assert.fail;
54 import static org.junit.Assume.assumeTrue;
55 
56 import android.app.ActivityManager;
57 import android.content.ComponentName;
58 import android.content.Context;
59 import android.content.Intent;
60 import android.os.Bundle;
61 import android.platform.test.annotations.Presubmit;
62 import android.server.wm.ActivityLauncher;
63 import android.server.wm.CommandSession.ActivitySession;
64 import android.server.wm.Condition;
65 import android.server.wm.MultiDisplayTestBase;
66 import android.server.wm.TestJournalProvider.TestJournalContainer;
67 import android.server.wm.WindowManagerState.DisplayContent;
68 import android.server.wm.WindowManagerState.Task;
69 import android.view.Display;
70 import android.view.View;
71 import android.view.ViewGroup;
72 import android.view.WindowManager;
73 
74 import com.android.compatibility.common.util.SystemUtil;
75 import com.android.compatibility.common.util.TestUtils;
76 
77 import org.junit.Before;
78 import org.junit.Test;
79 
80 /**
81  * Build/Install/Run:
82  *     atest CtsWindowManagerDeviceMultiDisplay:MultiDisplaySecurityTests
83  *
84  * Tests if be allowed to launch an activity on multi-display environment.
85  */
86 @Presubmit
87 @android.server.wm.annotation.Group3
88 public class MultiDisplaySecurityTests extends MultiDisplayTestBase {
89 
90     @Before
91     @Override
setUp()92     public void setUp() throws Exception {
93         super.setUp();
94         assumeTrue(supportsMultiDisplay());
95     }
96 
97     /**
98      * Tests launching an activity on a virtual display without special permission must not be
99      * allowed.
100      */
101     @Test
testLaunchWithoutPermissionOnVirtualDisplayByOwner()102     public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() {
103         // Create new virtual display.
104         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
105 
106         separateTestJournal();
107 
108         // Try to launch an activity and check if security exception was triggered.
109         getLaunchActivityBuilder()
110                 .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
111                 .setDisplayId(newDisplay.mId)
112                 .setTargetActivity(TEST_ACTIVITY)
113                 .execute();
114         assertSecurityExceptionFromActivityLauncher();
115         mWmState.computeState(TEST_ACTIVITY);
116         assertFalse("Restricted activity must not be launched",
117                 mWmState.containsActivity(TEST_ACTIVITY));
118     }
119 
120     /**
121      * Tests launching an activity on a virtual display without special permission must not be
122      * allowed.
123      */
124     @Test
testLaunchWithoutPermissionOnVirtualDisplay()125     public void testLaunchWithoutPermissionOnVirtualDisplay() {
126         // Create new virtual display.
127         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
128 
129         separateTestJournal();
130 
131         // Try to launch an activity and check it security exception was triggered.
132         getLaunchActivityBuilder()
133                 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
134                         SECOND_LAUNCH_BROADCAST_ACTION)
135                 .setDisplayId(newDisplay.mId)
136                 .setTargetActivity(TEST_ACTIVITY)
137                 .execute();
138         assertSecurityExceptionFromActivityLauncher();
139         mWmState.computeState(TEST_ACTIVITY);
140         assertFalse("Restricted activity must not be launched",
141                 mWmState.containsActivity(TEST_ACTIVITY));
142     }
143 
144     /**
145      * Tests launching an activity on virtual display and then launching another activity that
146      * doesn't allow embedding - it should fail with security exception.
147      */
148     @Test
testConsequentLaunchActivityFromVirtualDisplayNoEmbedding()149     public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() {
150         // Create new virtual display.
151         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
152 
153         // Launch activity on new secondary display.
154         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
155 
156         waitAndAssertActivityStateOnDisplay(LAUNCHING_ACTIVITY, STATE_RESUMED, newDisplay.mId,
157                 "Activity launched on secondary display must be resumed");
158 
159         separateTestJournal();
160 
161         // Launch second activity from app on secondary display specifying same display id.
162         getLaunchActivityBuilder()
163                 .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
164                 .setDisplayId(newDisplay.mId)
165                 .execute();
166 
167         assertSecurityExceptionFromActivityLauncher();
168     }
169 
isActivityStartAllowedOnDisplay(int displayId, ComponentName activity)170     private boolean isActivityStartAllowedOnDisplay(int displayId, ComponentName activity) {
171         final Intent intent = new Intent(Intent.ACTION_VIEW).setComponent(activity);
172         return mTargetContext.getSystemService(ActivityManager.class)
173                 .isActivityStartAllowedOnDisplay(mTargetContext, displayId, intent);
174     }
175 
176     /**
177      * Tests
178      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
179      * for simulated display. It is owned by system and is public, so should be accessible.
180      */
181     @Test
testCanAccessSystemOwnedDisplay()182     public void testCanAccessSystemOwnedDisplay()  {
183         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
184                 .setSimulateDisplay(true)
185                 .createDisplay();
186 
187         assertTrue(isActivityStartAllowedOnDisplay(newDisplay.mId, TEST_ACTIVITY));
188     }
189 
190     /**
191      * Tests
192      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
193      * for a public virtual display and an activity that doesn't support embedding from shell.
194      */
195     @Test
testCanAccessPublicVirtualDisplayWithInternalPermission()196     public void testCanAccessPublicVirtualDisplayWithInternalPermission() {
197         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
198                 .setPublicDisplay(true)
199                 .createDisplay();
200 
201         SystemUtil.runWithShellPermissionIdentity(
202                 () -> assertTrue(isActivityStartAllowedOnDisplay(
203                         newDisplay.mId, SECOND_NO_EMBEDDING_ACTIVITY)),
204                 "android.permission.INTERNAL_SYSTEM_WINDOW");
205     }
206 
207     /**
208      * Tests
209      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
210      * for a private virtual display and an activity that doesn't support embedding from shell.
211      */
212     @Test
testCanAccessPrivateVirtualDisplayWithInternalPermission()213     public void testCanAccessPrivateVirtualDisplayWithInternalPermission() {
214         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
215                 .setPublicDisplay(false)
216                 .createDisplay();
217 
218         SystemUtil.runWithShellPermissionIdentity(
219                 () -> assertTrue(isActivityStartAllowedOnDisplay(
220                         newDisplay.mId, SECOND_NO_EMBEDDING_ACTIVITY)),
221                 "android.permission.INTERNAL_SYSTEM_WINDOW");
222     }
223 
224     /**
225      * Tests
226      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
227      * for a public virtual display, an activity that supports embedding but the launching entity
228      * does not have required permission to embed an activity from other app.
229      */
230     @Test
testCantAccessPublicVirtualDisplayNoEmbeddingPermission()231     public void testCantAccessPublicVirtualDisplayNoEmbeddingPermission() {
232         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
233                 .setPublicDisplay(true)
234                 .createDisplay();
235 
236         assertFalse(isActivityStartAllowedOnDisplay(newDisplay.mId, SECOND_ACTIVITY));
237     }
238 
239     /**
240      * Tests
241      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
242      * for a public virtual display and an activity that does not support embedding.
243      */
244     @Test
testCantAccessPublicVirtualDisplayActivityEmbeddingNotAllowed()245     public void testCantAccessPublicVirtualDisplayActivityEmbeddingNotAllowed() {
246         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
247                 .setPublicDisplay(true)
248                 .createDisplay();
249 
250         SystemUtil.runWithShellPermissionIdentity(
251                 () -> assertFalse(isActivityStartAllowedOnDisplay(
252                         newDisplay.mId, SECOND_NO_EMBEDDING_ACTIVITY)),
253                 "android.permission.ACTIVITY_EMBEDDING");
254     }
255 
256     /**
257      * Tests
258      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
259      * for a public virtual display and an activity that supports embedding.
260      */
261     @Test
testCanAccessPublicVirtualDisplayActivityEmbeddingAllowed()262     public void testCanAccessPublicVirtualDisplayActivityEmbeddingAllowed() {
263         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
264                 .setPublicDisplay(true)
265                 .createDisplay();
266 
267         SystemUtil.runWithShellPermissionIdentity(
268                 () -> assertTrue(isActivityStartAllowedOnDisplay(
269                         newDisplay.mId, SECOND_ACTIVITY)),
270                 "android.permission.ACTIVITY_EMBEDDING");
271     }
272 
273     /**
274      * Tests
275      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
276      * for a private virtual display.
277      */
278     @Test
testCantAccessPrivateVirtualDisplay()279     public void testCantAccessPrivateVirtualDisplay() {
280         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
281                 .setPublicDisplay(false)
282                 .createDisplay();
283 
284         assertFalse(isActivityStartAllowedOnDisplay(newDisplay.mId, SECOND_ACTIVITY));
285     }
286 
287     /**
288      * Tests
289      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
290      * for a private virtual display to check the start of its own activity.
291      */
292     @Test
testCantAccessPrivateVirtualDisplayByOwner()293     public void testCantAccessPrivateVirtualDisplayByOwner() {
294         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
295                 .setPublicDisplay(false)
296                 .createDisplay();
297 
298         // Check the embedding call.
299         separateTestJournal();
300         mContext.sendBroadcast(new Intent(ACTION_TEST_ACTIVITY_START)
301                 .setPackage(LAUNCH_BROADCAST_RECEIVER.getPackageName())
302                 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
303                 .putExtra(EXTRA_COMPONENT_NAME, TEST_ACTIVITY)
304                 .putExtra(EXTRA_TARGET_DISPLAY, newDisplay.mId));
305 
306         assertActivityStartCheckResult(false);
307     }
308 
309     /**
310      * Tests
311      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
312      * for a private virtual display by UID present on that display and target activity that allows
313      * embedding.
314      */
315     @Test
testCanAccessPrivateVirtualDisplayByUidPresentOnDisplayActivityEmbeddingAllowed()316     public void testCanAccessPrivateVirtualDisplayByUidPresentOnDisplayActivityEmbeddingAllowed() {
317         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
318                 .setPublicDisplay(false)
319                 .createDisplay();
320         // Launch a test activity into the target display.
321         launchActivityOnDisplay(EMBEDDING_ACTIVITY, newDisplay.mId);
322 
323         // Check the embedding call.
324         separateTestJournal();
325         mContext.sendBroadcast(new Intent(ACTION_EMBEDDING_TEST_ACTIVITY_START)
326                 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
327                 .putExtra(EXTRA_EMBEDDING_COMPONENT_NAME, SECOND_ACTIVITY)
328                 .putExtra(EXTRA_EMBEDDING_TARGET_DISPLAY, newDisplay.mId));
329 
330         assertActivityStartCheckResult(true);
331     }
332 
333     /**
334      * Tests
335      * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
336      * for a private virtual display by UID present on that display and target activity that does
337      * not allow embedding.
338      */
339     @Test
testCanAccessPrivateVirtualDisplayByUidPresentOnDisplayActivityEmbeddingNotAllowed()340     public void testCanAccessPrivateVirtualDisplayByUidPresentOnDisplayActivityEmbeddingNotAllowed()
341             throws Exception {
342         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
343                 .setPublicDisplay(false)
344                 .createDisplay();
345         // Launch a test activity into the target display.
346         launchActivityOnDisplay(EMBEDDING_ACTIVITY, newDisplay.mId);
347 
348         // Check the embedding call.
349         separateTestJournal();
350         mContext.sendBroadcast(new Intent(ACTION_EMBEDDING_TEST_ACTIVITY_START)
351                 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
352                 .putExtra(EXTRA_EMBEDDING_COMPONENT_NAME, SECOND_NO_EMBEDDING_ACTIVITY)
353                 .putExtra(EXTRA_EMBEDDING_TARGET_DISPLAY, newDisplay.mId));
354 
355         assertActivityStartCheckResult(false);
356     }
357 
assertActivityStartCheckResult(boolean expected)358     private void assertActivityStartCheckResult(boolean expected) {
359         final String component = ActivityLauncher.TAG;
360         final Bundle resultExtras = Condition.waitForResult(
361                 new Condition<Bundle>("activity start check for " + component)
362                         .setRetryIntervalMs(500)
363                         .setResultSupplier(() -> TestJournalContainer.get(component).extras)
364                         .setResultValidator(extras -> extras.containsKey(
365                                 ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY)));
366         if (resultExtras != null) {
367             assertEquals("Activity start check must match", expected, resultExtras
368                     .getBoolean(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY));
369             return;
370         }
371         fail("Expected activity start check from " + component + " not found");
372     }
373 
374     @Test
testDisplayHasAccess_UIDCanPresentOnPrivateDisplay()375     public void testDisplayHasAccess_UIDCanPresentOnPrivateDisplay() {
376         final VirtualDisplayLauncher virtualDisplayLauncher =
377                 mObjectTracker.manage(new VirtualDisplayLauncher());
378         // Create a virtual private display.
379         final DisplayContent newDisplay = virtualDisplayLauncher
380                 .setPublicDisplay(false)
381                 .createDisplay();
382         // Launch an embeddable activity into the private display.
383         // Assert that the UID can present on display.
384         final ActivitySession session1 = virtualDisplayLauncher.launchActivityOnDisplay(
385                 DISPLAY_ACCESS_CHECK_EMBEDDING_ACTIVITY, newDisplay);
386         assertEquals(
387                 "Activity which the UID should accessible on private display",
388                 isUidAccessibleOnDisplay(session1),
389                 true);
390 
391         // Launch another embeddable activity with a different UID, verify that it will be
392         // able to access the display where it was put.
393         // Note that set withShellPermission as true in launchActivityOnDisplay is to
394         // make sure ACTIVITY_EMBEDDING can be granted by shell.
395         final ActivitySession session2 = virtualDisplayLauncher.launchActivityOnDisplay(
396                 SECOND_ACTIVITY, newDisplay,
397                 bundle -> bundle.putBoolean(EXTRA_DISPLAY_ACCESS_CHECK, true),
398                 true /* withShellPermission */, true /* waitForLaunch */);
399 
400         // Verify SECOND_ACTIVITY's UID has access to this virtual private display.
401         assertEquals(
402                 "Second activity which the UID should accessible on private display",
403                 isUidAccessibleOnDisplay(session2),
404                 true);
405     }
406 
407     @Test
testDisplayHasAccess_NoAccessWhenUIDNotPresentOnPrivateDisplay()408     public void testDisplayHasAccess_NoAccessWhenUIDNotPresentOnPrivateDisplay() {
409         final VirtualDisplayLauncher virtualDisplayLauncher =
410                 mObjectTracker.manage(new VirtualDisplayLauncher());
411         // Create a virtual private display.
412         final DisplayContent newDisplay = virtualDisplayLauncher
413                 .setPublicDisplay(false)
414                 .createDisplay();
415         // Launch an embeddable activity into the private display.
416         // Assume that the UID can access on display.
417         final ActivitySession session1 = virtualDisplayLauncher.launchActivityOnDisplay(
418                 DISPLAY_ACCESS_CHECK_EMBEDDING_ACTIVITY, newDisplay);
419         assertEquals(
420                 "Activity which the UID should accessible on private display",
421                 isUidAccessibleOnDisplay(session1),
422                 true);
423 
424         // Verify SECOND_NO_EMBEDDING_ACTIVITY's UID can't access this virtual private display
425         // since there is no entity with this UID on this display.
426         // Note that set withShellPermission as false in launchActivityOnDisplay is to
427         // prevent activity can launch when INTERNAL_SYSTEM_WINDOW granted by shell case.
428         separateTestJournal();
429         final ActivitySession session2 = virtualDisplayLauncher.launchActivityOnDisplay(
430                 SECOND_NO_EMBEDDING_ACTIVITY, newDisplay, null /* extrasConsumer */,
431                 false /* withShellPermission */, false /* waitForLaunch */);
432         assertEquals(
433                 "Second activity which the UID should not accessible on private display",
434                 isUidAccessibleOnDisplay(session2),
435                 false);
436     }
437 
438     @Test
testDisplayHasAccess_ExceptionWhenAddViewWithoutPresentOnPrivateDisplay()439     public void testDisplayHasAccess_ExceptionWhenAddViewWithoutPresentOnPrivateDisplay() {
440         // Create a virtual private display.
441         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
442                 .setPublicDisplay(false)
443                 .createDisplay();
444         try {
445             final Display display = mDm.getDisplay(newDisplay.mId);
446             final Context newDisplayContext = mContext.createDisplayContext(display);
447             newDisplayContext.getSystemService(WindowManager.class).addView(new View(mContext),
448                     new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
449         } catch (IllegalArgumentException e) {
450             // Exception happened when createDisplayContext with invalid display.
451             return;
452         }
453         fail("UID should not have access to private display without present entities.");
454     }
455 
isUidAccessibleOnDisplay(ActivitySession session)456     private boolean isUidAccessibleOnDisplay(ActivitySession session) {
457         boolean result = false;
458         try {
459             result = session.isUidAccessibleOnDisplay();
460         } catch (RuntimeException e) {
461             // Catch the exception while waiting reply (i.e. timeout)
462         }
463         return result;
464     }
465 
466     /** Test that shell is allowed to launch on secondary displays. */
467     @Test
testPermissionLaunchFromShell()468     public void testPermissionLaunchFromShell(){
469        // Create new virtual display.
470         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
471         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
472         mWmState.assertFocusedActivity("Virtual display activity must be on top",
473                 VIRTUAL_DISPLAY_ACTIVITY);
474         final int defaultDisplayFocusedTaskId = mWmState.getFocusedTaskId();
475         Task frontRootTask = mWmState.getRootTask(defaultDisplayFocusedTaskId);
476         assertEquals("Top root task must remain on primary display",
477                 DEFAULT_DISPLAY, frontRootTask.mDisplayId);
478 
479         // Launch activity on new secondary display.
480         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
481 
482         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
483                 "Test activity must be on secondary display");
484         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
485                 pair(newDisplay.mId, TEST_ACTIVITY));
486 
487         // Launch other activity with different uid and check it is launched on dynamic task on
488         // secondary display.
489         final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
490                 + " --display " + newDisplay.mId;
491         executeShellCommand(startCmd);
492 
493         waitAndAssertActivityStateOnDisplay(SECOND_ACTIVITY, STATE_RESUMED, newDisplay.mId,
494                 "Second activity must be on newly launched app");
495         assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
496                 pair(newDisplay.mId, SECOND_ACTIVITY));
497     }
498 
499     /** Test that launching from app that is on external display is allowed. */
500     @Test
testPermissionLaunchFromAppOnSecondary()501     public void testPermissionLaunchFromAppOnSecondary() {
502         // Create new simulated display.
503         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
504                 .setSimulateDisplay(true)
505                 .createDisplay();
506 
507         // Launch activity with different uid on secondary display.
508         final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
509                 + " --display " + newDisplay.mId;
510         executeShellCommand(startCmd);
511 
512         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
513                 "Top activity must be the newly launched one");
514 
515         // Launch another activity with third different uid from app on secondary display and
516         // check it is launched on secondary display.
517         getLaunchActivityBuilder()
518                 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
519                         SECOND_LAUNCH_BROADCAST_ACTION)
520                 .setDisplayId(newDisplay.mId)
521                 .setTargetActivity(THIRD_ACTIVITY)
522                 .execute();
523 
524         waitAndAssertTopResumedActivity(THIRD_ACTIVITY, newDisplay.mId,
525                 "Top activity must be the newly launched one");
526     }
527 
528     /** Tests that an activity can launch an activity from a different UID into its own task. */
529     @Test
testPermissionLaunchMultiUidTask()530     public void testPermissionLaunchMultiUidTask() {
531         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
532                 .setSimulateDisplay(true)
533                 .createDisplay();
534 
535         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
536         mWmState.computeState(LAUNCHING_ACTIVITY);
537 
538         // Check that the first activity is launched onto the secondary display.
539         final int frontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId);
540         Task frontTask = mWmState.getRootTask(frontRootTaskId);
541         assertEquals(
542                 "Activity launched on secondary display must be resumed",
543                 getActivityName(LAUNCHING_ACTIVITY),
544                 frontTask.getResumedActivity());
545         mWmState.assertFocusedRootTask("Top task must be on secondary display",
546                 frontRootTaskId);
547 
548         // Launch an activity from a different UID into the first activity's task.
549         getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute();
550 
551         waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
552                 "Top activity must be the newly launched one");
553         frontTask = mWmState.getRootTask(frontRootTaskId);
554         assertEquals("Secondary display must contain 1 task", 1,
555                 mWmState.getDisplay(newDisplay.mId).getRootTasks().size());
556     }
557 
558     /**
559      * Test that launching from app that is not present on external display and doesn't own it to
560      * that external display is not allowed.
561      */
562     @Test
testPermissionLaunchFromDifferentApp()563     public void testPermissionLaunchFromDifferentApp() {
564         // Create new virtual display.
565         final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
566         mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
567         mWmState.assertFocusedActivity("Virtual display activity must be focused",
568                 VIRTUAL_DISPLAY_ACTIVITY);
569         final int defaultDisplayFocusedRootTaskId = mWmState.getFocusedTaskId();
570         Task frontRootTask = mWmState.getRootTask(defaultDisplayFocusedRootTaskId);
571         assertEquals("Top root task must remain on primary display",
572                 DEFAULT_DISPLAY, frontRootTask.mDisplayId);
573 
574         // Launch activity on new secondary display.
575         launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
576         waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
577                 "Test activity must be the newly launched one");
578 
579         separateTestJournal();
580 
581         // Launch other activity with different uid and check security exception is triggered.
582         getLaunchActivityBuilder()
583                 .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER,
584                         SECOND_LAUNCH_BROADCAST_ACTION)
585                 .setDisplayId(newDisplay.mId)
586                 .setTargetActivity(THIRD_ACTIVITY)
587                 .execute();
588 
589         assertSecurityExceptionFromActivityLauncher();
590     }
591 
592     /**
593      * Test that only private virtual display can show content with insecure keyguard.
594      */
595     @Test
testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay()596     public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() {
597         // Try to create new show-with-insecure-keyguard public virtual display.
598         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
599                 .setPublicDisplay(true)
600                 .setCanShowWithInsecureKeyguard(true)
601                 .createDisplay(false /* mustBeCreated */);
602 
603         // Check that the display is not created.
604         assertNull(newDisplay);
605     }
606 
607     /**
608      * Test setting system decoration flag and show IME flag without sufficient permissions.
609      */
610     @Test
testSettingFlagWithoutInternalSystemPermission()611     public void testSettingFlagWithoutInternalSystemPermission() throws Exception {
612         // The reason to use a trusted display is that we can guarantee the security exception
613         // is coming from lacking internal system permission.
614         final DisplayContent trustedDisplay = createManagedVirtualDisplaySession()
615                 .setSimulateDisplay(true)
616                 .createDisplay();
617         final WindowManager wm = mTargetContext.getSystemService(WindowManager.class);
618 
619         // Verify setting system decorations flag without internal system permission.
620         try {
621             wm.setShouldShowSystemDecors(trustedDisplay.mId, true);
622 
623             // Unexpected result, restore flag to avoid affecting other tests.
624             wm.setShouldShowSystemDecors(trustedDisplay.mId, false);
625             TestUtils.waitUntil("Waiting for system decoration flag to be set",
626                     5 /* timeoutSecond */,
627                     () -> !wm.shouldShowSystemDecors(trustedDisplay.mId));
628             fail("Should not allow setting system decoration flag without internal system "
629                     + "permission");
630         } catch (SecurityException e) {
631             // Expected security exception.
632         }
633 
634         // Verify setting show IME flag without internal system permission.
635         try {
636             wm.setDisplayImePolicy(trustedDisplay.mId, DISPLAY_IME_POLICY_LOCAL);
637 
638             // Unexpected result, restore flag to avoid affecting other tests.
639             wm.setDisplayImePolicy(trustedDisplay.mId, DISPLAY_IME_POLICY_FALLBACK_DISPLAY);
640             TestUtils.waitUntil("Waiting for show IME flag to be set",
641                     5 /* timeoutSecond */,
642                     () -> (wm.getDisplayImePolicy(trustedDisplay.mId)
643                             == DISPLAY_IME_POLICY_FALLBACK_DISPLAY));
644             fail("Should not allow setting show IME flag without internal system permission");
645         } catch (SecurityException e) {
646             // Expected security exception.
647         }
648     }
649 
650     /**
651      * Test getting system decoration flag and show IME flag without sufficient permissions.
652      */
653     @Test
testGettingFlagWithoutInternalSystemPermission()654     public void testGettingFlagWithoutInternalSystemPermission() {
655         // The reason to use a trusted display is that we can guarantee the security exception
656         // is coming from lacking internal system permission.
657         final DisplayContent trustedDisplay = createManagedVirtualDisplaySession()
658                 .setSimulateDisplay(true)
659                 .createDisplay();
660         final WindowManager wm = mTargetContext.getSystemService(WindowManager.class);
661 
662         // Verify getting system decorations flag without internal system permission.
663         try {
664             wm.shouldShowSystemDecors(trustedDisplay.mId);
665             fail("Only allow internal system to get system decoration flag");
666         } catch (SecurityException e) {
667             // Expected security exception.
668         }
669 
670         // Verify getting show IME flag without internal system permission.
671         try {
672             wm.getDisplayImePolicy(trustedDisplay.mId);
673             fail("Only allow internal system to get show IME flag");
674         } catch (SecurityException e) {
675             // Expected security exception.
676         }
677     }
678 
679     /**
680      * Test setting system decoration flag and show IME flag to the untrusted display.
681      */
682     @Test
testSettingFlagToUntrustedDisplay()683     public void testSettingFlagToUntrustedDisplay() throws Exception {
684         final DisplayContent untrustedDisplay = createManagedVirtualDisplaySession()
685                 .createDisplay();
686         final WindowManager wm = mTargetContext.getSystemService(WindowManager.class);
687 
688         // Verify setting system decoration flag to an untrusted display.
689         getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
690         try {
691             wm.setShouldShowSystemDecors(untrustedDisplay.mId, true);
692 
693             // Unexpected result, restore flag to avoid affecting other tests.
694             wm.setShouldShowSystemDecors(untrustedDisplay.mId, false);
695             TestUtils.waitUntil("Waiting for system decoration flag to be set",
696                     5 /* timeoutSecond */,
697                     () -> !wm.shouldShowSystemDecors(untrustedDisplay.mId));
698             fail("Should not allow setting system decoration flag to the untrusted virtual "
699                     + "display");
700         } catch (SecurityException e) {
701             // Expected security exception.
702         } finally {
703             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
704         }
705 
706         // Verify setting show IME flag to an untrusted display.
707         getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
708         try {
709             wm.setDisplayImePolicy(untrustedDisplay.mId, DISPLAY_IME_POLICY_LOCAL);
710 
711             // Unexpected result, restore flag to avoid affecting other tests.
712             wm.setDisplayImePolicy(untrustedDisplay.mId, DISPLAY_IME_POLICY_FALLBACK_DISPLAY);
713             TestUtils.waitUntil("Waiting for show IME flag to be set",
714                     5 /* timeoutSecond */,
715                     () -> (wm.getDisplayImePolicy(untrustedDisplay.mId)
716                             == DISPLAY_IME_POLICY_FALLBACK_DISPLAY));
717             fail("Should not allow setting show IME flag to the untrusted virtual display");
718         } catch (SecurityException e) {
719             // Expected security exception.
720         } finally {
721             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
722         }
723     }
724 
725     /**
726      * Test getting system decoration flag and show IME flag from the untrusted display.
727      */
728     @Test
testGettingFlagFromUntrustedDisplay()729     public void testGettingFlagFromUntrustedDisplay() {
730         final DisplayContent untrustedDisplay = createManagedVirtualDisplaySession()
731                 .createDisplay();
732         final WindowManager wm = mTargetContext.getSystemService(WindowManager.class);
733 
734         // Verify getting system decoration flag from an untrusted display.
735         SystemUtil.runWithShellPermissionIdentity(() -> assertFalse(
736                 "Display should not support showing system decorations",
737                 wm.shouldShowSystemDecors(untrustedDisplay.mId)));
738 
739         // Verify getting show IME flag from an untrusted display.
740         SystemUtil.runWithShellPermissionIdentity(() -> assertEquals(
741                 "Display should not support showing IME window",
742                 wm.getDisplayImePolicy(untrustedDisplay.mId),
743                 DISPLAY_IME_POLICY_FALLBACK_DISPLAY));
744     }
745 
746     /**
747      * Test setting system decoration flag and show IME flag to the trusted display.
748      */
749     @Test
testSettingFlagToTrustedDisplay()750     public void testSettingFlagToTrustedDisplay() throws Exception {
751         final DisplayContent trustedDisplay = createManagedVirtualDisplaySession()
752                 .setSimulateDisplay(true)
753                 .createDisplay();
754         final WindowManager wm = mTargetContext.getSystemService(WindowManager.class);
755 
756         // Verify setting system decoration flag to a trusted display.
757         SystemUtil.runWithShellPermissionIdentity(() -> {
758             // Assume the display should not support system decorations by default.
759             assertFalse(wm.shouldShowSystemDecors(trustedDisplay.mId));
760 
761             try {
762                 wm.setShouldShowSystemDecors(trustedDisplay.mId, true);
763                 TestUtils.waitUntil("Waiting for system decoration flag to be set",
764                         5 /* timeoutSecond */,
765                         () -> wm.shouldShowSystemDecors(trustedDisplay.mId));
766 
767                 assertTrue(wm.shouldShowSystemDecors(trustedDisplay.mId));
768             } finally {
769                 // Restore flag to avoid affecting other tests.
770                 wm.setShouldShowSystemDecors(trustedDisplay.mId, false);
771                 TestUtils.waitUntil("Waiting for system decoration flag to be set",
772                         5 /* timeoutSecond */,
773                         () -> !wm.shouldShowSystemDecors(trustedDisplay.mId));
774             }
775         });
776 
777         // Verify setting show IME flag to a trusted display.
778         SystemUtil.runWithShellPermissionIdentity(() -> {
779             // Assume the display should not show IME window by default.
780             assertEquals(DISPLAY_IME_POLICY_FALLBACK_DISPLAY,
781                     wm.getDisplayImePolicy(trustedDisplay.mId));
782 
783             try {
784                 wm.setDisplayImePolicy(trustedDisplay.mId, DISPLAY_IME_POLICY_LOCAL);
785                 TestUtils.waitUntil("Waiting for show IME flag to be set",
786                         5 /* timeoutSecond */,
787                         () -> (wm.getDisplayImePolicy(trustedDisplay.mId)
788                                 == DISPLAY_IME_POLICY_LOCAL));
789 
790                 assertEquals(DISPLAY_IME_POLICY_LOCAL, wm.getDisplayImePolicy(trustedDisplay.mId));
791 
792                 wm.setDisplayImePolicy(trustedDisplay.mId, DISPLAY_IME_POLICY_HIDE);
793                 TestUtils.waitUntil("Waiting for show IME flag to be set",
794                         5 /* timeoutSecond */,
795                         () -> (wm.getDisplayImePolicy(trustedDisplay.mId)
796                                 == DISPLAY_IME_POLICY_HIDE));
797 
798                 assertEquals(DISPLAY_IME_POLICY_HIDE, wm.getDisplayImePolicy(trustedDisplay.mId));
799             } finally {
800                 // Restore flag to avoid affecting other tests.
801                 wm.setDisplayImePolicy(trustedDisplay.mId, DISPLAY_IME_POLICY_FALLBACK_DISPLAY);
802                 TestUtils.waitUntil("Waiting for show IME flag to be set",
803                         5 /* timeoutSecond */,
804                         () -> (wm.getDisplayImePolicy(trustedDisplay.mId)
805                                 == DISPLAY_IME_POLICY_FALLBACK_DISPLAY));
806             }
807         });
808     }
809 }
810