Allow untrusted embedding for EMBED_ANY_APP_IN_UNTRUSTED_MODE
Bug: 289199433
Test: atest TaskFragmentTest
Change-Id: Idb8e13d0e9357960a9ca92a225e4e979222d2e69
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cdb4aea..95e0e1b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -899,6 +899,9 @@
<!-- Permission required for Cts test - CtsNotificationTestCases -->
<uses-permission android:name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" />
+ <!-- Permission required for Cts test - CtsWindowManagerJetpackTestCases -->
+ <uses-permission android:name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE" />
+
<!-- Permission required for BinaryTransparencyService shell API and host test -->
<uses-permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b2b547e..11e7bb0 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -725,6 +726,9 @@
// TaskFragment to have bounds outside of the parent bounds.
return false;
}
+ if (hasEmbedAnyAppInUntrustedModePermission(mTaskFragmentOrganizerUid)) {
+ return true;
+ }
return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
}
@@ -796,6 +800,15 @@
}
/**
+ * Checks if a particular app uid has the {@link EMBED_ANY_APP_IN_UNTRUSTED_MODE} permission.
+ */
+ private static boolean hasEmbedAnyAppInUntrustedModePermission(int uid) {
+ return Flags.untrustedEmbeddingAnyAppPermission()
+ && checkPermission(EMBED_ANY_APP_IN_UNTRUSTED_MODE,
+ PermissionChecker.PID_UNKNOWN, uid) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
* Checks if all activities in the task fragment are embedded as fully trusted.
* @see #isFullyTrustedEmbedding(ActivityRecord, int)
* @param uid uid of the TaskFragment organizer.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 245b2c5..98ca094 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE;
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -24,11 +26,14 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -65,12 +70,14 @@
import androidx.test.filters.MediumTest;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.Collections;
import java.util.Set;
@@ -592,6 +599,70 @@
}
@Test
+ public void testIsAllowedToBeEmbeddedInTrustedMode_withManageActivityTasksPermission() {
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setCreateParentTask()
+ .createActivityCount(1)
+ .build();
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+
+ // Not allow embedding activity if not a trusted host.
+ assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
+ taskFragment.isAllowedToEmbedActivity(activity));
+
+ MockitoSession session =
+ mockitoSession().spyStatic(ActivityTaskManagerService.class).startMocking();
+ try {
+ doReturn(PERMISSION_GRANTED).when(() -> {
+ return ActivityTaskManagerService.checkPermission(
+ eq(MANAGE_ACTIVITY_TASKS), anyInt(), anyInt());
+ });
+ // With the MANAGE_ACTIVITY_TASKS permission, trusted embedding is always allowed.
+ assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode());
+ } finally {
+ session.finishMocking();
+ }
+ }
+
+ @Test
+ public void testIsAllowedToEmbedActivityInUntrustedMode_withUntrustedEmbeddingAnyAppPermission(
+ ) {
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setCreateParentTask()
+ .createActivityCount(1)
+ .build();
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+
+ // Not allow embedding activity if not a trusted host.
+ assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
+ taskFragment.isAllowedToEmbedActivity(activity));
+
+ MockitoSession session =
+ mockitoSession()
+ .spyStatic(ActivityTaskManagerService.class)
+ .spyStatic(Flags.class)
+ .startMocking();
+ try {
+ doReturn(PERMISSION_GRANTED).when(() -> {
+ return ActivityTaskManagerService.checkPermission(
+ eq(EMBED_ANY_APP_IN_UNTRUSTED_MODE), anyInt(), anyInt());
+ });
+ // With the EMBED_ANY_APP_IN_UNTRUSTED_MODE permission, untrusted embedding is always
+ // allowed, but it doesn't always allow trusted embedding.
+ doReturn(true).when(() -> Flags.untrustedEmbeddingAnyAppPermission());
+ assertTrue(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity));
+ assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity));
+
+ // If the flag is disabled, the permission doesn't have effect.
+ doReturn(false).when(() -> Flags.untrustedEmbeddingAnyAppPermission());
+ assertFalse(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity));
+ assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity));
+ } finally {
+ session.finishMocking();
+ }
+ }
+
+ @Test
public void testIgnoreRequestedOrientationForActivityEmbeddingSplit() {
// Setup two activities in ActivityEmbedding split.
final Task task = createTask(mDisplayContent);