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