diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 791aeb73ffa3c..909ca3988c5d0 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3505,6 +3505,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1805116444": { + "message": "We don't support remote animation for Task with multiple TaskFragmentOrganizers.", + "level": "ERROR", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/AppTransitionController.java" + }, "1810019902": { "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", "level": "DEBUG", diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 82377b34d22a5..10ed8dc3dcb6f 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -533,40 +533,81 @@ public class AppTransitionController { * * @return {@code true} if the transition is overridden. */ - @VisibleForTesting - boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, + private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, ArraySet activityTypes) { final ArrayList allWindows = new ArrayList<>(); allWindows.addAll(mDisplayContent.mClosingApps); allWindows.addAll(mDisplayContent.mOpeningApps); allWindows.addAll(mDisplayContent.mChangingContainers); - // Find the common TaskFragmentOrganizer of all windows. - ITaskFragmentOrganizer organizer = null; + // It should only animated by the organizer if all windows are below the same leaf Task. + Task leafTask = null; for (int i = allWindows.size() - 1; i >= 0; i--) { final ActivityRecord r = getAppFromContainer(allWindows.get(i)); if (r == null) { return false; } + // The activity may be a child of embedded Task, but we want to find the owner Task. + // As a result, find the organized TaskFragment first. final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment(); - final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null - ? organizedTaskFragment.getTaskFragmentOrganizer() - : null; - if (curOrganizer == null) { - // All windows must below an organized TaskFragment. + // There are also cases where the Task contains non-embedded activity, such as launching + // split TaskFragments from a non-embedded activity. + // The hierarchy may looks like this: + // - Task + // - Activity + // - TaskFragment + // - Activity + // - TaskFragment + // - Activity + // We also want to have the organizer handle the transition for such case. + final Task task = organizedTaskFragment != null + ? organizedTaskFragment.getTask() + : r.getTask(); + if (task == null) { return false; } - if (organizer == null) { - organizer = curOrganizer; - } else if (!organizer.asBinder().equals(curOrganizer.asBinder())) { - // They must be controlled by the same organizer. + // We don't want the organizer to handle transition of other non-embedded Task. + if (leafTask != null && leafTask != task) { return false; } + final ActivityRecord rootActivity = task.getRootActivity(); + // We don't want the organizer to handle transition when the whole app is closing. + if (rootActivity == null) { + return false; + } + // We don't want the organizer to handle transition of non-embedded activity of other + // app. + if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) { + return false; + } + leafTask = task; + } + if (leafTask == null) { + return false; } - final RemoteAnimationDefinition definition = organizer != null + // We don't support remote animation for Task with multiple TaskFragmentOrganizers. + final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1]; + final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> { + final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer(); + if (tfOrganizer == null) { + return false; + } + if (organizer[0] != null && !organizer[0].asBinder().equals(tfOrganizer.asBinder())) { + return true; + } + organizer[0] = tfOrganizer; + return false; + }); + if (hasMultipleOrganizers) { + ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for" + + " Task with multiple TaskFragmentOrganizers."); + return false; + } + + final RemoteAnimationDefinition definition = organizer[0] != null ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController - .getRemoteAnimationDefinition(organizer) + .getRemoteAnimationDefinition(organizer[0]) : null; final RemoteAnimationAdapter adapter = definition != null ? definition.getAdapter(transit, activityTypes) 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 d6d7f07b2ef30..5fa76bb2e25b3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -27,6 +27,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.annotation.Nullable; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -763,59 +766,148 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test - public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() { - // TaskFragmentOrganizer registers remote animation. + public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); - final ITaskFragmentOrganizer iOrganizer = - ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); - final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( new TestRemoteAnimationRunner(), 10, 1); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); - mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); - mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition); + setupTaskFragmentRemoteAnimation(organizer, adapter); // Create a TaskFragment with embedded activity. - final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) - .setParentTask(createTask(mDisplayContent)) - .createActivityCount(1) - .setOrganizer(organizer) - .build(); + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity( + createTask(mDisplayContent), organizer); final ActivityRecord activity = taskFragment.getTopMostActivity(); activity.allDrawn = true; spyOn(mDisplayContent.mAppTransition); - // Prepare a transition for TaskFragment. - mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); - mDisplayContent.mOpeningApps.add(activity); - mDisplayContent.mChangingContainers.add(taskFragment); - mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + // Prepare a transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); - // Check if the transition has been overridden. + // Should be overridden. verify(mDisplayContent.mAppTransition) .overridePendingAppTransitionRemote(adapter, false /* sync */); } + @Test + public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing non-embedded activity. + final ActivityRecord closingActivity = createActivityRecord(task); + closingActivity.allDrawn = true; + // Opening TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should be overridden. + verify(mDisplayContent.mAppTransition) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment1.getTopMostActivity(); + closingActivity.allDrawn = true; + closingActivity.info.applicationInfo.uid = 12345; + // Opening TaskFragment with embedded activity with different UID. + final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment2.getTopMostActivity(); + openingActivity.info.applicationInfo.uid = 54321; + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1); + + // Should be overridden. + verify(mDisplayContent.mAppTransition) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + // Closing activity in Task1. + final ActivityRecord closingActivity = createActivityRecord(mDisplayContent); + closingActivity.allDrawn = true; + // Opening TaskFragment with embedded activity in Task2. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity( + createTask(mDisplayContent), organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition for TaskFragment. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should not be overridden. + verify(mDisplayContent.mAppTransition, never()) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment.getTopMostActivity(); + closingActivity.allDrawn = true; + closingActivity.info.applicationInfo.uid = 12345; + // Opening non-embedded activity with different UID. + final ActivityRecord openingActivity = createActivityRecord(task); + openingActivity.info.applicationInfo.uid = 54321; + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should not be overridden + verify(mDisplayContent.mAppTransition, never()) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + @Test public void testTransitionGoodToGoForTaskFragments() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final Task task = createTask(mDisplayContent); - final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm) - .setParentTask(task) - .createActivityCount(1) - .setOrganizer(organizer) - .build(); + final TaskFragment changeTaskFragment = + createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(organizer) .build(); changeTaskFragment.getTopMostActivity().allDrawn = true; - mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); - mDisplayContent.mChangingContainers.add(changeTaskFragment); spyOn(mDisplayContent.mAppTransition); spyOn(emptyTaskFragment); - mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + prepareAndTriggerAppTransition( + null /* openingActivity */, null /* closingActivity*/, changeTaskFragment); // Transition not ready because there is an empty non-finishing TaskFragment. verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any()); @@ -829,4 +921,34 @@ public class AppTransitionControllerTest extends WindowTestsBase { // removed. verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any()); } + + /** Registers remote animation for the organizer. */ + private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer, + RemoteAnimationAdapter adapter) { + final ITaskFragmentOrganizer iOrganizer = + ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); + final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter); + mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); + mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition); + } + + private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity, + @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) { + if (openingActivity != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_OPEN, 0); + mDisplayContent.mOpeningApps.add(openingActivity); + } + if (closingActivity != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CLOSE, 0); + mDisplayContent.mClosingApps.add(closingActivity); + } + if (changingTaskFragment != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); + mDisplayContent.mChangingContainers.add(changingTaskFragment); + } + mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + } } \ No newline at end of file diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 6626aa46e7dae..8ec1bd6c57870 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -699,6 +699,15 @@ class WindowTestsBase extends SystemServiceTestsBase { return builder.build(); } + static TaskFragment createTaskFragmentWithEmbeddedActivity(@NonNull Task parentTask, + TaskFragmentOrganizer organizer) { + return new TaskFragmentBuilder(parentTask.mAtmService) + .setParentTask(parentTask) + .createActivityCount(1) + .setOrganizer(organizer) + .build(); + } + /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ DisplayContent createNewDisplay() { return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);