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

Commit 5b5463bc authored by Chris Li's avatar Chris Li
Browse files

Allow input during remote animation if the app is fully trusted

We disabled input during client remote animation for security reason,
but it will also disable user from fast tapping. For now, we don't
disable input of the app is system app or if it only embeds itself for
the major use case

Fix: 231406322
Bug: 197364677
Test: atest WmTests:AppTransitionControllerTest
Change-Id: Ibf1036ae80e80097a2ede58d7fffbb65c1510f68
parent c9ab8713
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -662,11 +662,16 @@ public class AppTransitionController {
                "Override with TaskFragment remote animation for transit=%s",
                "Override with TaskFragment remote animation for transit=%s",
                AppTransition.appTransitionOldToString(transit));
                AppTransition.appTransitionOldToString(transit));


        final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController
                .getTaskFragmentOrganizerUid(organizer);
        final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding(
                organizerUid);
        final RemoteAnimationController remoteAnimationController =
        final RemoteAnimationController remoteAnimationController =
                mDisplayContent.mAppTransition.getRemoteAnimationController();
                mDisplayContent.mAppTransition.getRemoteAnimationController();
        if (remoteAnimationController != null) {
        if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) {
            // We are going to use client-driven animation, Disable all input on activity windows
            // We are going to use client-driven animation, Disable all input on activity windows
            // during the animation to ensure it is safe to allow client to animate the surfaces.
            // during the animation (unless it is fully trusted) to ensure it is safe to allow
            // client to animate the surfaces.
            // This is needed for all activity windows in the animation Task.
            // This is needed for all activity windows in the animation Task.
            remoteAnimationController.setOnRemoteAnimationReady(() -> {
            remoteAnimationController.setOnRemoteAnimationReady(() -> {
                final Consumer<ActivityRecord> updateActivities =
                final Consumer<ActivityRecord> updateActivities =
+24 −9
Original line number Original line Diff line number Diff line
@@ -562,13 +562,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     * @param uid   uid of the TaskFragment organizer.
     * @param uid   uid of the TaskFragment organizer.
     */
     */
    boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a, int uid) {
    boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a, int uid) {
        if (UserHandle.getAppId(uid) == SYSTEM_UID) {
        if (isFullyTrustedEmbedding(a, uid)) {
            // The system is trusted to embed other apps securely and for all users.
            return true;
        }

        if (uid == a.getUid()) {
            // Activities from the same UID can be embedded freely by the host.
            return true;
            return true;
        }
        }


@@ -586,14 +580,35 @@ class TaskFragment extends WindowContainer<WindowContainer> {
                knownActivityEmbeddingCerts);
                knownActivityEmbeddingCerts);
    }
    }


    /**
     * It is fully trusted for embedding in the system app or embedding in the same app. This is
     * different from {@link #isAllowedToBeEmbeddedInTrustedMode()} since there may be a small
     * chance for a previous trusted app to start doing something bad.
     */
    private static boolean isFullyTrustedEmbedding(@NonNull ActivityRecord a, int uid) {
        // The system is trusted to embed other apps securely and for all users.
        return UserHandle.getAppId(uid) == SYSTEM_UID
                // Activities from the same UID can be embedded freely by the host.
                || uid == a.getUid();
    }

    /**
     * Checks if all activities in the task fragment are embedded as fully trusted.
     * @see #isFullyTrustedEmbedding(ActivityRecord, int)
     * @param uid   uid of the TaskFragment organizer.
     */
    boolean isFullyTrustedEmbedding(int uid) {
        // Traverse all activities to see if any of them are not fully trusted embedding.
        return !forAllActivities(r -> !isFullyTrustedEmbedding(r, uid));
    }

    /**
    /**
     * Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
     * Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
     */
     */
    boolean isAllowedToBeEmbeddedInTrustedMode() {
    boolean isAllowedToBeEmbeddedInTrustedMode() {
        // Traverse all activities to see if any of them are not in the trusted mode.
        // Traverse all activities to see if any of them are not in the trusted mode.
        final Predicate<ActivityRecord> callback = r -> !isAllowedToEmbedActivityInTrustedMode(r);
        return !forAllActivities(r -> !isAllowedToEmbedActivityInTrustedMode(r));
        return !forAllActivities(callback);
    }
    }


    /**
    /**
+5 −0
Original line number Original line Diff line number Diff line
@@ -378,6 +378,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
        }
        }
    }
    }


    int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
        return state.mOrganizerUid;
    }

    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
        if (!state.addTaskFragment(taskFragment)) {
        if (!state.addTaskFragment(taskFragment)) {
+41 −2
Original line number Original line Diff line number Diff line
@@ -1128,6 +1128,41 @@ public class AppTransitionControllerTest extends WindowTestsBase {
        verify(activity).setDropInputMode(DropInputMode.NONE);
        verify(activity).setDropInputMode(DropInputMode.NONE);
    }
    }


    /**
     * We don't need to drop input for fully trusted embedding (system app, and embedding in the
     * same app). This will allow users to do fast tapping.
     */
    @Test
    public void testOverrideTaskFragmentAdapter_noInputProtectedForFullyTrustedAnimation() {
        final Task task = createTask(mDisplayContent);
        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
        setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);

        // Create a TaskFragment with only trusted embedded activity
        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .createActivityCount(1)
                .setOrganizer(organizer)
                .build();
        final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
        prepareActivityForAppTransition(activity);
        final int uid = mAtm.mTaskFragmentOrganizerController.getTaskFragmentOrganizerUid(
                getITaskFragmentOrganizer(organizer));
        doReturn(true).when(task).isFullyTrustedEmbedding(uid);
        spyOn(mDisplayContent.mAppTransition);

        // Prepare and start transition.
        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();

        // The animation will be animated remotely by client, but input should not be dropped for
        // fully trusted.
        assertTrue(remoteAnimationRunner.isAnimationStarted());
        verify(activity, never()).setDropInputForAnimation(true);
        verify(activity, never()).setDropInputMode(DropInputMode.ALL);
    }

    @Test
    @Test
    public void testTransitionGoodToGoForTaskFragments() {
    public void testTransitionGoodToGoForTaskFragments() {
        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
@@ -1197,8 +1232,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
            TestRemoteAnimationRunner remoteAnimationRunner) {
            TestRemoteAnimationRunner remoteAnimationRunner) {
        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                remoteAnimationRunner, 10, 1);
                remoteAnimationRunner, 10, 1);
        final ITaskFragmentOrganizer iOrganizer =
        final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
@@ -1208,6 +1242,11 @@ public class AppTransitionControllerTest extends WindowTestsBase {
                definition);
                definition);
    }
    }


    private static ITaskFragmentOrganizer getITaskFragmentOrganizer(
            TaskFragmentOrganizer organizer) {
        return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
    }

    private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity,
    private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity,
            @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) {
            @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) {
        if (openingActivity != null) {
        if (openingActivity != null) {