Merge "Enforce onActivityReparentedToTask" into tm-qpr-dev
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 509b1e6..2e716ae 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -34,6 +34,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -322,9 +323,10 @@
+ " is not in a task belong to the organizer app.");
return null;
}
- if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
+ if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED
+ || !task.isAllowedToEmbedActivityInTrustedMode(activity, mOrganizerUid)) {
Slog.d(TAG, "Reparent activity=" + activity.token
- + " is not allowed to be embedded.");
+ + " is not allowed to be embedded in trusted mode.");
return null;
}
@@ -350,7 +352,7 @@
activity.token, task.mTaskId);
return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK)
.setTaskId(task.mTaskId)
- .setActivityIntent(activity.intent)
+ .setActivityIntent(trimIntent(activity.intent))
.setActivityToken(activityToken);
}
@@ -1095,4 +1097,15 @@
return false;
}
}
+
+ /**
+ * Trims the given Intent to only those that are needed to for embedding rules. This helps to
+ * make it safer for cross-uid embedding even if we only send the Intent for trusted embedding.
+ */
+ private static Intent trimIntent(@NonNull Intent intent) {
+ return new Intent()
+ .setComponent(intent.getComponent())
+ .setPackage(intent.getPackage())
+ .setAction(intent.getAction());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 4202f46..c535182 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -62,10 +62,12 @@
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -403,7 +405,7 @@
final TaskFragmentTransaction.Change change = changes.get(0);
assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType());
assertEquals(task.mTaskId, change.getTaskId());
- assertEquals(activity.intent, change.getActivityIntent());
+ assertIntentsEqualForOrganizer(activity.intent, change.getActivityIntent());
assertNotEquals(activity.token, change.getActivityToken());
mTransaction.reparentActivityToTaskFragment(mFragmentToken, change.getActivityToken());
assertApplyTransactionAllowed(mTransaction);
@@ -415,6 +417,62 @@
}
@Test
+ public void testOnActivityReparentedToTask_untrustedEmbed_notReported() {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid,
+ DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
+ mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
+ final Task task = createTask(mDisplayContent);
+ task.addChild(mTaskFragment, POSITION_TOP);
+ final ActivityRecord activity = createActivityRecord(task);
+
+ // Make sure the activity is embedded in untrusted mode.
+ activity.info.applicationInfo.uid = uid + 1;
+ doReturn(pid + 1).when(activity).getPid();
+ task.effectiveUid = uid;
+ doReturn(EMBEDDING_ALLOWED).when(task).isAllowedToEmbedActivity(activity, uid);
+ doReturn(false).when(task).isAllowedToEmbedActivityInTrustedMode(activity, uid);
+ doReturn(true).when(task).isAllowedToEmbedActivityInUntrustedMode(activity);
+
+ // Notify organizer if it was embedded before entered Pip.
+ // Create a temporary token since the activity doesn't belong to the same process.
+ clearInvocations(mOrganizer);
+ activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
+ mController.onActivityReparentedToTask(activity);
+ mController.dispatchPendingEvents();
+
+ // Disallow organizer to reparent activity that is untrusted embedded.
+ verify(mOrganizer, never()).onTransactionReady(mTransactionCaptor.capture());
+ }
+
+ @Test
+ public void testOnActivityReparentedToTask_trimReportedIntent() {
+ // Make sure the activity pid/uid is the same as the organizer caller.
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final Task task = activity.getTask();
+ activity.info.applicationInfo.uid = uid;
+ doReturn(pid).when(activity).getPid();
+ task.effectiveUid = uid;
+ activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
+
+ // Test the Intent trim in #assertIntentTrimmed
+ activity.intent.setComponent(new ComponentName("TestPackage", "TestClass"))
+ .setPackage("TestPackage")
+ .setAction("TestAction")
+ .setData(mock(Uri.class))
+ .putExtra("Test", 123)
+ .setFlags(10);
+
+ mController.onActivityReparentedToTask(activity);
+ mController.dispatchPendingEvents();
+
+ assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token);
+ }
+
+ @Test
public void testRegisterRemoteAnimations() {
mController.registerRemoteAnimations(mIOrganizer, TASK_ID, mDefinition);
@@ -1425,7 +1483,8 @@
final TaskFragmentTransaction.Change change = changes.remove(0);
assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType());
assertEquals(taskId, change.getTaskId());
- assertEquals(intent, change.getActivityIntent());
+ assertIntentsEqualForOrganizer(intent, change.getActivityIntent());
+ assertIntentTrimmed(change.getActivityIntent());
assertEquals(activityToken, change.getActivityToken());
}
@@ -1452,4 +1511,17 @@
mockParent.lastActiveTime = 100;
doReturn(true).when(mockParent).shouldBeVisible(any());
}
+
+ private static void assertIntentsEqualForOrganizer(@NonNull Intent expected,
+ @NonNull Intent actual) {
+ assertEquals(expected.getComponent(), actual.getComponent());
+ assertEquals(expected.getPackage(), actual.getPackage());
+ assertEquals(expected.getAction(), actual.getAction());
+ }
+
+ private static void assertIntentTrimmed(@NonNull Intent intent) {
+ assertNull(intent.getData());
+ assertNull(intent.getExtras());
+ assertEquals(0, intent.getFlags());
+ }
}