Ensuring multi-window callbacks contain new configuration and are in order

- This CL has two main changes:
  1) It modifies the activity multi-window and picture-in-picture mode
     changed callbacks to provide the configuration of the activity with
     the mode applied.
  2) It modifies the order in which the multi-window and picture-in-picture
     mode callbacks are made, to ensure that when going in and out of
     picture-in-picture: first PiP, then MW, and then the config change.
- Previously, the ordering of the two callbacks was inconsistent.  When
  calling moveActivityToPinnedStack(), we reparent the task into the pinned
  stack (triggering the picture-in-picture mode change), followed by the
  resize animation (causes configuration changes).  Inversely, when we
  expand the task to fullscreen (and not just remove it), we run the
  animation first, which resizes the task to the final size (causes
  configuration changes) then reparent after the animation completes
  (triggering the picture-in-picture mode change).

  In this CL, we ensure that for both the transition in and out of PiP, we
  defer to the bounds animation to trigger the PiP mode change.  Normal
  calls to reparent or adding a new task are unchanged.  When the PiP
  mode change is called from the animation, it provides the final target
  bounds which we use to calculate the target configuration of the activity
  for the callback.  If the bounds animation is interrupted, an update will
  also be scheduled if we change the fullscreen state we are animating to.

  To work around the issue where we are scheduling MW/PiP mode changes in
  both the animation and the configuration change, we also now keep track
  of each state internally in the ActivityRecord.

Bug: 36099777
Test: android.server.cts.ActivityManagerPinnedStackTests
Test: #testConfigurationChangeOrderDuringTransition

Change-Id: I03513bc3a4d4a72c250983f22f079ce9d7a2cb40
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b623b2f..af3f352 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -371,6 +371,10 @@
      * application */
     final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
 
+    /** The target stack bounds for the picture-in-picture mode changed that we need to report to
+     * the application */
+    Rect mPipModeChangedTargetStackBounds;
+
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
 
@@ -2333,6 +2337,9 @@
             ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
             final boolean isFullscreenStackVisible = fullscreenStack != null &&
                     fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+            // If we are moving from the pinned stack, then the animation takes care of updating
+            // the picture-in-picture mode.
+            final boolean schedulePictureInPictureModeChange = (fromStackId != PINNED_STACK_ID);
             final ArrayList<TaskRecord> tasks = stack.getAllTasks();
             final int size = tasks.size();
             if (onTop) {
@@ -2351,6 +2358,7 @@
                     // Defer resume until all the tasks have been moved to the fullscreen stack
                     task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
                             REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
+                            schedulePictureInPictureModeChange,
                             "moveTasksToFullscreenStack - onTop");
                 }
             } else {
@@ -2360,8 +2368,9 @@
                             ? Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0;
                     // Defer resume until all the tasks have been moved to the fullscreen stack
                     task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, position,
-                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
-                            DEFER_RESUME, "moveTasksToFullscreenStack - NOT_onTop");
+                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
+                            schedulePictureInPictureModeChange,
+                            "moveTasksToFullscreenStack - NOT_onTop");
                 }
             }
 
@@ -2855,9 +2864,9 @@
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
                 }
-                // Defer resume until below
+                // Defer resume until below, and do not schedule PiP changes until we animate below
                 task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                        DEFER_RESUME, reason);
+                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
             } else {
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
@@ -2872,9 +2881,9 @@
                         r.mActivityType);
                 r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
 
-                // Defer resume until below
+                // Defer resume until below, and do not schedule PiP changes until we animate below
                 newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                        DEFER_RESUME, reason);
+                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
             }
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
@@ -4108,7 +4117,7 @@
         mActivityMetricsLogger.logWindowState();
     }
 
-    void scheduleReportMultiWindowModeChanged(TaskRecord task) {
+    void scheduleUpdateMultiWindowMode(TaskRecord task) {
         for (int i = task.mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = task.mActivities.get(i);
             if (r.app != null && r.app.thread != null) {
@@ -4121,22 +4130,39 @@
         }
     }
 
-    void scheduleReportPictureInPictureModeChangedIfNeeded(TaskRecord task, ActivityStack prevStack) {
+    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
         final ActivityStack stack = task.getStack();
         if (prevStack == null || prevStack == stack
                 || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
             return;
         }
 
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
-            if (r.app != null && r.app.thread != null) {
-                mPipModeChangedActivities.add(r);
-            }
-        }
+        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
+    }
 
-        if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
-            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
+            boolean immediate) {
+
+        if (immediate) {
+            mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
+            for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+                final ActivityRecord r = task.mActivities.get(i);
+                if (r.app != null && r.app.thread != null) {
+                    r.updatePictureInPictureMode(targetStackBounds);
+                }
+            }
+        } else {
+            for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+                final ActivityRecord r = task.mActivities.get(i);
+                if (r.app != null && r.app.thread != null) {
+                    mPipModeChangedActivities.add(r);
+                }
+            }
+            mPipModeChangedTargetStackBounds = targetStackBounds;
+
+            if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+                mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+            }
         }
     }
 
@@ -4164,7 +4190,7 @@
                     synchronized (mService) {
                         for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
                             final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
-                            r.scheduleMultiWindowModeChanged();
+                            r.updateMultiWindowMode();
                         }
                     }
                 } break;
@@ -4172,7 +4198,7 @@
                     synchronized (mService) {
                         for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                             final ActivityRecord r = mPipModeChangedActivities.remove(i);
-                            r.schedulePictureInPictureModeChanged();
+                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
                         }
                     }
                 } break;