Add API to check if activity can be started on a display

Some activity launch restrictions apply to virtual displays, which
are not communicated to apps. Currently there is no way to check
this in advance before starting an activity. This means that an app
get an unexpected SecurityException after calling startActivity and
therefore cannot know when to show in their UI a possible option to
launch on a secondary display.

This CL gives adds a public API to check the possibility of launch
on a specific display.

Test: ActivityManagerMultiDisplayTests
Bug: 119575501
Change-Id: Ieb70f0bb79b1a88b7284a19af2efeeb1fcb90f75
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9861157..782fad9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1426,6 +1426,42 @@
     }
 
     /**
+     * Public API to check if the client is allowed to start an activity on specified display.
+     *
+     * If the target display is private or virtual, some restrictions will apply.
+     *
+     * @param displayId Target display id.
+     * @param intent Intent used to launch the activity.
+     * @param resolvedType The MIME type of the intent.
+     * @param userId The id of the user for whom the call is made.
+     * @return {@code true} if a call to start an activity on the target display should succeed and
+     *         no {@link SecurityException} will be thrown, {@code false} otherwise.
+     */
+    @Override
+    public final boolean isActivityStartAllowedOnDisplay(int displayId, Intent intent,
+            String resolvedType, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+
+        try {
+            // Collect information about the target of the Intent.
+            ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
+                    0 /* startFlags */, null /* profilerInfo */, userId,
+                    ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
+                            UserHandle.USER_NULL));
+            aInfo = mAmInternal.getActivityInfoForUser(aInfo, userId);
+
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
+                        aInfo);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
      * This is the internal entry point for handling Activity.finish().
      *
      * @param token The Binder token referencing the Activity we want to finish.