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