Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d6dabd2 authored by Chris Li's avatar Chris Li
Browse files

Enforce onActivityReparentedToTask

1. Only allow when the activity is trusted embedded, because sharing
   ComponentName of untrusted embedded is not safe.
2. Trim the activity Intent to those that are needed and safe for
   Activity Filter.

Bug: 255701223
Test: atest WmTests:TaskFragmentOrganizerControllerTest
Change-Id: I0c03b95a2b7a9c91315d09a77dc4b855e464a2f5
parent 63a2cda3
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static java.util.Objects.requireNonNull;
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 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
                        + " 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 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
                    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 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
            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());
    }
}
+74 −2
Original line number Diff line number Diff line
@@ -62,10 +62,12 @@ import static org.mockito.Mockito.never;
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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        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);
@@ -414,6 +416,62 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
                change.getActivityToken()));
    }

    @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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        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());
    }
}