Allow organizer to handle transition for non-embedded activity
We want to allow organizer to handle the transition in case like
launching split TaskFragments from a non-embedded activity.
Bug: 200496784
Test: atest WmTests:AppTransitionControllerTest
Test: test with demo app to launch split from non-embedded
Change-Id: Iaf1d07c8cf43fa773fede3c031855085147be263
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 791aeb7..909ca39 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 82377b3..10ed8dc 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 @@
*
* @return {@code true} if the transition is overridden.
*/
- @VisibleForTesting
- boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+ private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
final ArrayList<WindowContainer> 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 d6d7f07..5fa76bb 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_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.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 @@
}
@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 @@
// 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 6626aa4..8ec1bd6c 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 @@
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);