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