Move PIP/MW mode callbacks to be on the client side

We now infer the PIP/MW mode change from the new configuration.
Note also that both
- Activity#isInPictureInPictureMode
- Activity#isInMultiWindowMode
infer the current state from the configuration rather than querying
against the WM.

Also in this CL:
- When in removePinnedStackInSurfaceTransaction, keep the pinned stack
hidden till the windowing mode is set to fullscreen, this is to surpress
the attempt to set the activities to be started in reparenting
- When in ActivityRecord#shouldBeVisible, should take account the force
hidden flag, which is not actually in use before

Bug: 144097203
Bug: 142282126
Bug: 138329093
Test: atest ActivityLifecyclePipTests \
            ActivityLifecycleSplitScreenTests \
            ActivityLifecycleTopResumedStateTests \
            PinnedStackTests \
            SplitScreenTests \
            ActivityTaskManagerServiceTests \
            RecentsAnimationTest \
            AssistantStackTests \
            StartActivityTests \
            ActivityVisibilityTests \
            MultiDisplaySecurityTests \
            MultiDisplaySystemDecorationTests
Change-Id: Ibe032b5e50ba5c6d6bc44ebb54d07ac974ebe656
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dcdbfded..7bbe5cc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -235,10 +235,8 @@
 import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.MoveToDisplayItem;
-import android.app.servertransaction.MultiWindowModeChangeItem;
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.PipModeChangeItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.StartActivityItem;
 import android.app.servertransaction.StopActivityItem;
@@ -1155,11 +1153,6 @@
             return;
         }
 
-        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
-            // Don't do anything if we are currently deferring multi-window mode change.
-            return;
-        }
-
         // An activity is considered to be in multi-window mode if its task isn't fullscreen.
         final boolean inMultiWindowMode = inMultiWindowMode();
         if (inMultiWindowMode != mLastReportedMultiWindowMode) {
@@ -1167,20 +1160,13 @@
                 updatePictureInPictureMode(null, false);
             } else {
                 mLastReportedMultiWindowMode = inMultiWindowMode;
-                scheduleMultiWindowModeChanged(getConfiguration());
+                computeConfigurationAfterMultiWindowModeChange();
+                ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
+                        true /* ignoreVisibility */);
             }
         }
     }
 
-    private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
-        try {
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
-        } catch (Exception e) {
-            // If process died, I don't care.
-        }
-    }
-
     void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
         if (task == null || task.getStack() == null || !attachedToProcess()) {
             return;
@@ -1188,39 +1174,26 @@
 
         final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
         if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
-            // Picture-in-picture mode change normal triggers also multi-window mode change
-            // except transitions between pip and split screen mode, so update that here in order.
-            // Set the last reported MW state to the same as the PiP state since we haven't yet
-            // actually resized the task (these callbacks need to proceed the configuration change
-            // from the resize).
-            // TODO(110009072): Once we move these callbacks to the client, remove all logic related
-            // to forcing the update of the picture-in-picture mode as a part of the PiP animation.
-            final boolean shouldScheduleMultiWindowModeChange =
-                    mLastReportedMultiWindowMode != inMultiWindowMode();
+            // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
+            // update that here in order. Set the last reported MW state to the same as the PiP
+            // state since we haven't yet actually resized the task (these callbacks need to
+            // precede the configuration change from the resize.
             mLastReportedPictureInPictureMode = inPictureInPictureMode;
             mLastReportedMultiWindowMode = inPictureInPictureMode;
-            final Configuration newConfig = new Configuration();
             if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
-                newConfig.setTo(task.getRequestedOverrideConfiguration());
-                Rect outBounds = newConfig.windowConfiguration.getBounds();
-                task.adjustForMinimalTaskDimensions(outBounds, outBounds);
-                task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
+                computeConfigurationAfterMultiWindowModeChange();
             }
-            schedulePictureInPictureModeChanged(newConfig);
-            if (shouldScheduleMultiWindowModeChange) {
-                scheduleMultiWindowModeChanged(newConfig);
-            }
+            ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
+                    true /* ignoreVisibility */);
         }
     }
 
-    private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
-        try {
-            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
-                            overrideConfig));
-        } catch (Exception e) {
-            // If process died, no one cares.
-        }
+    private void computeConfigurationAfterMultiWindowModeChange() {
+        final Configuration newConfig = new Configuration();
+        newConfig.setTo(task.getRequestedOverrideConfiguration());
+        Rect outBounds = newConfig.windowConfiguration.getBounds();
+        task.adjustForMinimalTaskDimensions(outBounds, outBounds);
+        task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
     }
 
     Task getTask() {
@@ -4531,6 +4504,15 @@
             return false;
         }
 
+        // Activity in a pinned stack should not be visible if the stack is in force hidden state.
+        // Typically due to the FLAG_FORCE_HIDDEN_FOR_PINNED_TASK set on the stack, which is a
+        // work around to send onStop before windowing mode change callbacks.
+        // See also ActivityStackSupervisor#removePinnedStackInSurfaceTransaction
+        // TODO: Should we ever be visible if the stack/task is invisible?
+        if (inPinnedWindowingMode() && stack.isForceHidden()) {
+            return false;
+        }
+
         // Check if the activity is on a sleeping display, and if it can turn it ON.
         if (getDisplay().isSleeping()) {
             final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn()
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2ab03ce..46b7b28 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3344,23 +3344,6 @@
         getDisplayContent().getPinnedStackController().setActions(actions);
     }
 
-    /**
-     * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
-     *         bounds and we have a deferred PiP mode changed callback set with the animation.
-     */
-    public boolean deferScheduleMultiWindowModeChanged() {
-        if (inPinnedWindowingMode()) {
-            // For the pinned stack, the deferring of the multi-window mode changed is tied to the
-            // transition animation into picture-in-picture, and is called once the animation
-            // completes, or is interrupted in a way that would leave the stack in a non-fullscreen
-            // state.
-            // @see BoundsAnimationController
-            // @see BoundsAnimationControllerTests
-            return (mBoundsAnimatingRequested || mBoundsAnimating);
-        }
-        return false;
-    }
-
     public boolean isForceScaled() {
         return mBoundsAnimating;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index fe9e5f3c..6f0a339 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1452,16 +1452,15 @@
         /**
          * Workaround: Force-stop all the activities in the pinned stack before we reparent them
          * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
-         * that the client receives onStop() before it is reparented.  We do this by detaching
-         * the stack from the display so that it will be considered invisible when
-         * ensureActivitiesVisible() is called, and all of its activities will be marked
-         * invisible as well and added to the stopping list.  After which we process the
+         * that the client receives onStop() before new windowing mode is set.
+         * We do this by detaching the stack from the display so that it will be considered
+         * invisible when ensureActivitiesVisible() is called, and all of its activities will be
+         * marked invisible as well and added to the stopping list.  After which we process the
          * stopping list by handling the idle.
          */
         stack.cancelAnimation();
         stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
         stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-        stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
         activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                 true /* processPausingActivities */, null /* configuration */);
 
@@ -1478,6 +1477,9 @@
                 toDisplay.getDefaultTaskDisplayArea().positionStackAtBottom(stack);
             }
 
+            // Follow on the workaround: activities are kept force hidden till the new windowing
+            // mode is set.
+            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
             mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
             mRootWindowContainer.resumeFocusedStacksTopActivities();
         } finally {
@@ -2242,12 +2244,6 @@
     }
 
     void scheduleUpdateMultiWindowMode(Task task) {
-        // If the stack is animating in a way where we will be forcing a multi-mode change at the
-        // end, then ensure that we defer all in between multi-window mode changes
-        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
-            return;
-        }
-
         final PooledConsumer c = PooledLambda.obtainConsumer(
                 ActivityStackSupervisor::addToMultiWindowModeChangedList, this,
                 PooledLambda.__(ActivityRecord.class));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5220fb2..511baa5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4036,36 +4036,8 @@
         }
     }
 
-    @Override
-    public boolean isInMultiWindowMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
-                return r.inMultiWindowMode();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean isInPictureInPictureMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    private boolean isInPictureInPictureMode(ActivityRecord r) {
+    @VisibleForTesting
+    boolean isInPictureInPictureMode(ActivityRecord r) {
         return r != null
                 && r.getRootTask() != null
                 && r.inPinnedWindowingMode()
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1036af6..51357d1 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -23,11 +23,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
 
@@ -391,8 +389,7 @@
     public boolean inMultiWindowMode() {
         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
                 mFullConfiguration.windowConfiguration.getWindowingMode();
-        return windowingMode != WINDOWING_MODE_FULLSCREEN
-                && windowingMode != WINDOWING_MODE_UNDEFINED;
+        return WindowConfiguration.inMultiWindowMode(windowingMode);
     }
 
     /** Returns true if this container is currently in split-screen windowing mode. */