Loading core/java/android/app/ActivityOptions.java +4 −2 Original line number Diff line number Diff line Loading @@ -1793,8 +1793,10 @@ public class ActivityOptions extends ComponentOptions { /** * Sets the id of the display where the activity should be launched. * An app can launch activities on public displays or displays where the app already has * activities. Otherwise, trying to launch on a private display or providing an invalid display * id will result in an exception. * activities. Otherwise, trying to launch on a display for which * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)} * returns {@code false} (such as a private display or providing an invalid display id) will * result in an exception. * <p> * Setting launch display id will be ignored on devices that don't have * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}. Loading services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +8 −0 Original line number Diff line number Diff line Loading @@ -1281,6 +1281,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return false; } if (DesktopExperienceFlags.ENABLE_MIRROR_DISPLAY_NO_ACTIVITY.isTrue()) { if (!displayContent.mDisplay.canHostTasks()) { Slog.w(TAG, "Launch on display check: activity launch is not allowed on a " + "display that cannot host tasks"); return false; } } // Check if the caller has enough privileges to embed activities and launch to private // displays. final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid, Loading services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +27 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; Loading Loading @@ -1069,6 +1070,32 @@ public class ActivityStarterTests extends WindowTestsBase { verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean()); } /** * This test ensures that activity launch on a secondary display that cannot host tasks is * disallowed, and a SecurityException should be thrown. */ @Test public void testStartActivityOnDisplayCannotHostTasks() { final ActivityStarter starter = prepareStarter(0); // Create a display that cannot host tasks. final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1500) .setCanHostTasks(false).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(secondaryDisplay.mDisplayId); assertThrows(SecurityException.class, () -> starter.setReason("testStartActivityOnDisplayCannotHostTasks") .setIntent(activity.intent) .setActivityOptions(options.toBundle(), Binder.getCallingPid(), Binder.getCallingUid()) .execute()); } @Test public void testWasVisibleInRestartAttempt() { final ActivityStarter starter = prepareStarter( Loading services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +26 −1 Original line number Diff line number Diff line Loading @@ -334,7 +334,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { * Ensures that a trusted display can launch arbitrary activity and an untrusted display can't. */ @Test public void testDisplayCanLaunchActivities() { public void testDisplayCanLaunchActivities_trustedDisplay() { final Display display = mDisplayContent.mDisplay; // An empty info without FLAG_ALLOW_EMBEDDED. final ActivityInfo activityInfo = new ActivityInfo(); Loading @@ -355,6 +355,31 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { assertThat(allowedOnUntrusted).isFalse(); } /** * Ensures that an arbitrary activity can be launched on a display the can host tasks, and * cannot be launched on a display that cannot host tasks. */ @EnableFlags(Flags.FLAG_ENABLE_MIRROR_DISPLAY_NO_ACTIVITY) @Test public void testDisplayCanLaunchActivities_canHostTasksDisplay() { final Display display = mDisplayContent.mDisplay; // An empty info without FLAG_ALLOW_EMBEDDED. final ActivityInfo activityInfo = new ActivityInfo(); final int callingPid = 12345; final int callingUid = 12345; spyOn(display); doReturn(true).when(display).canHostTasks(); final boolean allowedOnCanHostTasks = mSupervisor.isCallerAllowedToLaunchOnDisplay( callingPid, callingUid, display.getDisplayId(), activityInfo); assertThat(allowedOnCanHostTasks).isTrue(); doReturn(false).when(display).canHostTasks(); final boolean allowedOnCannotHostTasks = mSupervisor.isCallerAllowedToLaunchOnDisplay( callingPid, callingUid, display.getDisplayId(), activityInfo); assertThat(allowedOnCannotHostTasks).isFalse(); } /** * Verifies that process state will be updated with pending top without activity state change. * E.g. switch focus between resumed activities in multi-window mode. Loading services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +9 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ class TestDisplayContent extends DisplayContent { private SettingsEntry mOverrideSettings; @NonNull private DeviceStateController mDeviceStateController = mock(DeviceStateController.class); private boolean mCanHostTasks = true; Builder(ActivityTaskManagerService service, int width, int height) { mService = service; Loading Loading @@ -160,6 +161,10 @@ class TestDisplayContent extends DisplayContent { mInfo.ownerUid = ownerUid; return this; } Builder setCanHostTasks(boolean canHostTasks) { mCanHostTasks = canHostTasks; return this; } Builder setCutout(int left, int top, int right, int bottom) { final int cutoutFillerSize = 80; Rect boundLeft = left != 0 ? new Rect(0, 0, left, cutoutFillerSize) : null; Loading Loading @@ -215,6 +220,10 @@ class TestDisplayContent extends DisplayContent { mInfo.displayId = displayId; final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); spyOn(display); if (mCanHostTasks) { doReturn(true).when(display).canHostTasks(); } final TestDisplayContent newDisplay = createInternal(display); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); Loading Loading
core/java/android/app/ActivityOptions.java +4 −2 Original line number Diff line number Diff line Loading @@ -1793,8 +1793,10 @@ public class ActivityOptions extends ComponentOptions { /** * Sets the id of the display where the activity should be launched. * An app can launch activities on public displays or displays where the app already has * activities. Otherwise, trying to launch on a private display or providing an invalid display * id will result in an exception. * activities. Otherwise, trying to launch on a display for which * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)} * returns {@code false} (such as a private display or providing an invalid display id) will * result in an exception. * <p> * Setting launch display id will be ignored on devices that don't have * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}. Loading
services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +8 −0 Original line number Diff line number Diff line Loading @@ -1281,6 +1281,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return false; } if (DesktopExperienceFlags.ENABLE_MIRROR_DISPLAY_NO_ACTIVITY.isTrue()) { if (!displayContent.mDisplay.canHostTasks()) { Slog.w(TAG, "Launch on display check: activity launch is not allowed on a " + "display that cannot host tasks"); return false; } } // Check if the caller has enough privileges to embed activities and launch to private // displays. final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid, Loading
services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +27 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; Loading Loading @@ -1069,6 +1070,32 @@ public class ActivityStarterTests extends WindowTestsBase { verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean()); } /** * This test ensures that activity launch on a secondary display that cannot host tasks is * disallowed, and a SecurityException should be thrown. */ @Test public void testStartActivityOnDisplayCannotHostTasks() { final ActivityStarter starter = prepareStarter(0); // Create a display that cannot host tasks. final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1500) .setCanHostTasks(false).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(secondaryDisplay.mDisplayId); assertThrows(SecurityException.class, () -> starter.setReason("testStartActivityOnDisplayCannotHostTasks") .setIntent(activity.intent) .setActivityOptions(options.toBundle(), Binder.getCallingPid(), Binder.getCallingUid()) .execute()); } @Test public void testWasVisibleInRestartAttempt() { final ActivityStarter starter = prepareStarter( Loading
services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +26 −1 Original line number Diff line number Diff line Loading @@ -334,7 +334,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { * Ensures that a trusted display can launch arbitrary activity and an untrusted display can't. */ @Test public void testDisplayCanLaunchActivities() { public void testDisplayCanLaunchActivities_trustedDisplay() { final Display display = mDisplayContent.mDisplay; // An empty info without FLAG_ALLOW_EMBEDDED. final ActivityInfo activityInfo = new ActivityInfo(); Loading @@ -355,6 +355,31 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { assertThat(allowedOnUntrusted).isFalse(); } /** * Ensures that an arbitrary activity can be launched on a display the can host tasks, and * cannot be launched on a display that cannot host tasks. */ @EnableFlags(Flags.FLAG_ENABLE_MIRROR_DISPLAY_NO_ACTIVITY) @Test public void testDisplayCanLaunchActivities_canHostTasksDisplay() { final Display display = mDisplayContent.mDisplay; // An empty info without FLAG_ALLOW_EMBEDDED. final ActivityInfo activityInfo = new ActivityInfo(); final int callingPid = 12345; final int callingUid = 12345; spyOn(display); doReturn(true).when(display).canHostTasks(); final boolean allowedOnCanHostTasks = mSupervisor.isCallerAllowedToLaunchOnDisplay( callingPid, callingUid, display.getDisplayId(), activityInfo); assertThat(allowedOnCanHostTasks).isTrue(); doReturn(false).when(display).canHostTasks(); final boolean allowedOnCannotHostTasks = mSupervisor.isCallerAllowedToLaunchOnDisplay( callingPid, callingUid, display.getDisplayId(), activityInfo); assertThat(allowedOnCannotHostTasks).isFalse(); } /** * Verifies that process state will be updated with pending top without activity state change. * E.g. switch focus between resumed activities in multi-window mode. Loading
services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +9 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ class TestDisplayContent extends DisplayContent { private SettingsEntry mOverrideSettings; @NonNull private DeviceStateController mDeviceStateController = mock(DeviceStateController.class); private boolean mCanHostTasks = true; Builder(ActivityTaskManagerService service, int width, int height) { mService = service; Loading Loading @@ -160,6 +161,10 @@ class TestDisplayContent extends DisplayContent { mInfo.ownerUid = ownerUid; return this; } Builder setCanHostTasks(boolean canHostTasks) { mCanHostTasks = canHostTasks; return this; } Builder setCutout(int left, int top, int right, int bottom) { final int cutoutFillerSize = 80; Rect boundLeft = left != 0 ? new Rect(0, 0, left, cutoutFillerSize) : null; Loading Loading @@ -215,6 +220,10 @@ class TestDisplayContent extends DisplayContent { mInfo.displayId = displayId; final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); spyOn(display); if (mCanHostTasks) { doReturn(true).when(display).canHostTasks(); } final TestDisplayContent newDisplay = createInternal(display); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); Loading