Merge "Decide on navigation to task by policy in Virtual Device" into tm-dev
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index 1270d87..d3cc918 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -110,6 +110,13 @@
             @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.
      * Returns {@code false} if the Activity cannot remain on the display and the activity task will
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 f4c24a8..27de8cd 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 @@
     @NonNull
     private final ArraySet<UserHandle> mAllowedUsers;
     @Nullable
+    private final ArraySet<ComponentName> mAllowedCrossTaskNavigations;
+    @Nullable
+    private final ArraySet<ComponentName> mBlockedCrossTaskNavigations;
+    @Nullable
     private final ArraySet<ComponentName> mAllowedActivities;
     @Nullable
     private final ArraySet<ComponentName> mBlockedActivities;
@@ -100,6 +104,10 @@
      * @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 GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
             @NonNull ArraySet<UserHandle> allowedUsers,
+            @NonNull Set<ComponentName> allowedCrossTaskNavigations,
+            @NonNull Set<ComponentName> blockedCrossTaskNavigations,
             @NonNull Set<ComponentName> allowedActivities,
             @NonNull Set<ComponentName> blockedActivities,
             @ActivityPolicy int defaultActivityPolicy,
@@ -122,6 +132,8 @@
             @NonNull Consumer<ActivityInfo> activityBlockedCallback) {
         super();
         mAllowedUsers = allowedUsers;
+        mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations);
+        mBlockedCrossTaskNavigations = new ArraySet<>(blockedCrossTaskNavigations);
         mAllowedActivities = new ArraySet<>(allowedActivities);
         mBlockedActivities = new ArraySet<>(blockedActivities);
         mDefaultActivityPolicy = defaultActivityPolicy;
@@ -154,6 +166,46 @@
     }
 
     @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) {
         if (!canContainActivity(activityInfo, windowFlags, 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 235d286..90c879a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -533,6 +533,8 @@
                     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 d72e0ba..dfc6fa9 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 java.io.PrintWriter;
 import java.text.DateFormat;
-import java.util.ArrayList;
 import java.util.Date;
 
 /**
@@ -2046,12 +2045,13 @@
             final DisplayContent displayContent = mRootWindowContainer.getDisplayContentOrCreate(
                     mPreferredTaskDisplayArea.getDisplayId());
             if (displayContent != null && displayContent.mDwpcHelper.hasController()) {
-                final ArrayList<ActivityInfo> 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 27d46ec..2deb828 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -75,6 +75,19 @@
     }
 
     /**
+     * @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)
      */
     boolean keepActivityOnWindowFlagsChanged(ActivityInfo aInfo, int flagChanges,
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 75faf45..cd836c7 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 @@
                 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 a8282600..02009b7 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 @@
     }
 
     @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();
@@ -204,6 +204,13 @@
         ArraySet<Integer> 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<ActivityInfo> activities,
                 @WindowConfiguration.WindowingMode int windowingMode) {
             final int activityCount = activities.size();