diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java index 1270d87e3a044..d3cc918c2a29b 100644 --- a/core/java/android/window/DisplayWindowPolicyController.java +++ b/core/java/android/window/DisplayWindowPolicyController.java @@ -109,6 +109,13 @@ public abstract class DisplayWindowPolicyController { public abstract boolean canContainActivities(@NonNull List activities, @WindowConfiguration.WindowingMode int windowingMode); + /** + * Returns {@code true} if the given new task can be launched on this virtual display. + */ + public abstract boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo, + @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, + boolean isNewTask); + /** * Called when an Activity window is layouted with the new changes where contains the * window flags that we’re interested in. diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index f4c24a82ae139..27de8cdc0421d 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -76,6 +76,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull private final ArraySet mAllowedUsers; @Nullable + private final ArraySet mAllowedCrossTaskNavigations; + @Nullable + private final ArraySet mBlockedCrossTaskNavigations; + @Nullable private final ArraySet mAllowedActivities; @Nullable private final ArraySet mBlockedActivities; @@ -100,6 +104,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param windowFlags The window flags that this controller is interested in. * @param systemWindowFlags The system window flags that this controller is interested in. * @param allowedUsers The set of users that are allowed to stream in this display. + * @param allowedCrossTaskNavigations The set of components explicitly allowed to navigate + * across tasks on this device. + * @param blockedCrossTaskNavigations The set of components explicitly blocked from + * navigating across tasks on this device. * @param allowedActivities The set of activities explicitly allowed to stream on this device. * Used only if the {@code activityPolicy} is * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_BLOCKED}. @@ -115,6 +123,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController */ public GenericWindowPolicyController(int windowFlags, int systemWindowFlags, @NonNull ArraySet allowedUsers, + @NonNull Set allowedCrossTaskNavigations, + @NonNull Set blockedCrossTaskNavigations, @NonNull Set allowedActivities, @NonNull Set blockedActivities, @ActivityPolicy int defaultActivityPolicy, @@ -122,6 +132,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull Consumer activityBlockedCallback) { super(); mAllowedUsers = allowedUsers; + mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations); + mBlockedCrossTaskNavigations = new ArraySet<>(blockedCrossTaskNavigations); mAllowedActivities = new ArraySet<>(allowedActivities); mBlockedActivities = new ArraySet<>(blockedActivities); mDefaultActivityPolicy = defaultActivityPolicy; @@ -153,6 +165,46 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController return true; } + @Override + public boolean canActivityBeLaunched(ActivityInfo activityInfo, + @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, + boolean isNewTask) { + if (!isWindowingModeSupported(windowingMode)) { + return false; + } + + final ComponentName activityComponent = activityInfo.getComponentName(); + if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) { + // The error dialog alerting users that streaming is blocked is always allowed. + return true; + } + + if (!canContainActivity(activityInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { + mActivityBlockedCallback.accept(activityInfo); + return false; + } + + if (launchingFromDisplayId == Display.DEFAULT_DISPLAY) { + return true; + } + if (isNewTask && !mBlockedCrossTaskNavigations.isEmpty() + && mBlockedCrossTaskNavigations.contains(activityComponent)) { + Slog.d(TAG, "Virtual device blocking cross task navigation of " + activityComponent); + mActivityBlockedCallback.accept(activityInfo); + return false; + } + if (isNewTask && !mAllowedCrossTaskNavigations.isEmpty() + && !mAllowedCrossTaskNavigations.contains(activityComponent)) { + Slog.d(TAG, "Virtual device not allowing cross task navigation of " + + activityComponent); + mActivityBlockedCallback.accept(activityInfo); + return false; + } + + return true; + } + + @Override public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index e4b839af8b2e8..bd7e8f2e5fba2 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -528,6 +528,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(), + mParams.getAllowedCrossTaskNavigations(), + mParams.getBlockedCrossTaskNavigations(), mParams.getAllowedActivities(), mParams.getBlockedActivities(), mParams.getDefaultActivityPolicy(), diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 602e4160a679c..ad901c3579956 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -135,7 +135,6 @@ import com.android.server.wm.LaunchParamsController.LaunchParams; import java.io.PrintWriter; import java.text.DateFormat; -import java.util.ArrayList; import java.util.Date; /** @@ -2036,12 +2035,13 @@ class ActivityStarter { final DisplayContent displayContent = mRootWindowContainer.getDisplayContentOrCreate( mPreferredTaskDisplayArea.getDisplayId()); if (displayContent != null && displayContent.mDwpcHelper.hasController()) { - final ArrayList activities = new ArrayList<>(); - activities.add(r.info); final int targetWindowingMode = (targetTask != null) ? targetTask.getWindowingMode() : displayContent.getWindowingMode(); + final int launchingFromDisplayId = + mSourceRecord != null ? mSourceRecord.getDisplayId() : DEFAULT_DISPLAY; if (!displayContent.mDwpcHelper - .canContainActivities(activities, targetWindowingMode)) { + .canActivityBeLaunched(r.info, targetWindowingMode, launchingFromDisplayId, + newTask)) { Slog.w(TAG, "Abort to launch " + r.info.getComponentName() + " on display area " + mPreferredTaskDisplayArea); return START_ABORTED; diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java index 27d46ecfb91a1..2deb8286f95f7 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java +++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java @@ -74,6 +74,19 @@ class DisplayWindowPolicyControllerHelper { return mDisplayWindowPolicyController.canContainActivities(activities, windowingMode); } + /** + * @see DisplayWindowPolicyController#canActivityBeLaunched(ActivityInfo, int, int, boolean) + */ + public boolean canActivityBeLaunched(ActivityInfo activityInfo, + @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, + boolean isNewTask) { + if (mDisplayWindowPolicyController == null) { + return true; + } + return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, windowingMode, + launchingFromDisplayId, isNewTask); + } + /** * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int) */ diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index 75faf45541474..cd836c77076db 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -75,6 +75,8 @@ public class VirtualAudioControllerTest { FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, /* allowedUsers= */ new ArraySet<>(), + /* allowedCrossTaskNavigations= */ new ArraySet<>(), + /* blockedCrossTaskNavigations= */ new ArraySet<>(), /* allowedActivities= */ new ArraySet<>(), /* blockedActivities= */ new ArraySet<>(), VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index a82826006f178..02009b75e319d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -170,7 +170,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { } @Test - public void testCanContainActivities() { + public void testCanActivityBeLaunched() { ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm, mSupervisor, mock(ActivityStartInterceptor.class)); final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build(); @@ -203,6 +203,13 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { int mTopActivityUid = UserHandle.USER_NULL; ArraySet mRunningUids = new ArraySet<>(); + @Override + public boolean canActivityBeLaunched(@NonNull ActivityInfo activity, + @WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId, + boolean isNewTask) { + return false; + } + @Override public boolean canContainActivities(@NonNull List activities, @WindowConfiguration.WindowingMode int windowingMode) {