Start activity in the embedded TaskFragment if allowed

If the task already contains child TaskFragments, an activity
should be started in the child TaskFragments, but only doing so
if the activity is allowed to be embedded.

Currently, an app can embed its activities into its own task.
Only system or apps with ACTIVITY_EMBEDDING permission can embed
activities of another app.

Bug: 193456000
Bug: 189386461
Test: wm presubmit
Change-Id: I8707f5f79deb23cc9b2b8d647382416dce6a6e9d
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5c9fdc3..c90eba0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.ACTIVITY_EMBEDDING;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
@@ -2717,6 +2718,31 @@
         mIntentDelivered = true;
     }
 
+    /**
+     * Return {@code true} if the {@param task} has child {@code TaskFragment}s and the
+     * {@param activity} can be embedded in. Otherwise, return {@code false}
+     */
+    private boolean canActivityBeEmbedded(@NonNull ActivityRecord activity, @NonNull Task task) {
+        if (task.isLeafTaskFragment()) {
+            return false;
+        }
+
+        final int taskUid = task.effectiveUid;
+        // Allowing the activity be embedded into leaf TaskFragment if the task is owned by system.
+        if (taskUid == Process.SYSTEM_UID) {
+            return true;
+        }
+
+        // Allowing embedding if the task is owned by an app that has the ACTIVITY_EMBEDDING
+        // permission
+        if (mService.checkPermission(ACTIVITY_EMBEDDING, -1, taskUid) == PERMISSION_GRANTED) {
+            return true;
+        }
+
+        // Allowing embedding if the activity is from the same app that owned the task
+        return activity.isUid(taskUid);
+    }
+
     private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
         TaskFragment newParent = task;
         if (mInTaskFragment != null) {
@@ -2728,7 +2754,12 @@
             } else {
                 newParent = mInTaskFragment;
             }
+        } else if (canActivityBeEmbedded(mStartActivity, task)) {
+            // Use the child TaskFragment (if any) as the new parent if the activity can be embedded
+            final ActivityRecord top = task.topRunningActivity();
+            newParent = top != null ? top.getTaskFragment() : task;
         }
+
         if (mStartActivity.getTaskFragment() == null
                 || mStartActivity.getTaskFragment() == newParent) {
             newParent.addChild(mStartActivity, POSITION_TOP);