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