diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index cb6559715c558..69063557cfc38 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -663,11 +663,16 @@ public class AppTransitionController { "Override with TaskFragment remote animation for transit=%s", AppTransition.appTransitionOldToString(transit)); + final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController + .getTaskFragmentOrganizerUid(organizer); + final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding( + organizerUid); final RemoteAnimationController remoteAnimationController = mDisplayContent.mAppTransition.getRemoteAnimationController(); - if (remoteAnimationController != null) { + if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) { // 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. remoteAnimationController.setOnRemoteAnimationReady(() -> { final Consumer updateActivities = diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 56e96fa1fe58e..22df8b06e398b 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -562,13 +562,7 @@ class TaskFragment extends WindowContainer { * @param uid uid of the TaskFragment organizer. */ boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a, int uid) { - if (UserHandle.getAppId(uid) == SYSTEM_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. + if (isFullyTrustedEmbedding(a, uid)) { return true; } @@ -586,14 +580,35 @@ class TaskFragment extends WindowContainer { 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. * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord) */ boolean isAllowedToBeEmbeddedInTrustedMode() { // Traverse all activities to see if any of them are not in the trusted mode. - final Predicate callback = r -> !isAllowedToEmbedActivityInTrustedMode(r); - return !forAllActivities(callback); + return !forAllActivities(r -> !isAllowedToEmbedActivityInTrustedMode(r)); } /** diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 47e606a5313ee..b4d1cf77919a8 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -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) { final TaskFragmentOrganizerState state = validateAndGetState(organizer); if (!state.addTaskFragment(taskFragment)) { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 8474a36dc681a..77f884c936824 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -1128,6 +1128,41 @@ public class AppTransitionControllerTest extends WindowTestsBase { 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 public void testTransitionGoodToGoForTaskFragments() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); @@ -1197,8 +1232,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { TestRemoteAnimationRunner remoteAnimationRunner) { final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( remoteAnimationRunner, 10, 1); - final ITaskFragmentOrganizer iOrganizer = - ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); + final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer); final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter); @@ -1208,6 +1242,11 @@ public class AppTransitionControllerTest extends WindowTestsBase { definition); } + private static ITaskFragmentOrganizer getITaskFragmentOrganizer( + TaskFragmentOrganizer organizer) { + return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); + } + private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity, @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) { if (openingActivity != null) {