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);