WindowContainerTransaction: Provide non-BLAST bounds change sync

This is to enable landing the SysUI driven PiP animation without BLAST. Without
BLAST we can't support multi container sync, multi buffer sync, etc, but we can
support the same sort of synchronization we've supported before. Let's recall how
the old PIP animation works: At the end of the enter animation, or the beginning
of the leave animation, the Surface changes size (When the Windowing mode changes).
We detect this size change deep in the guts of WindowStateAnimator, and when
we detect that we are called from "relayoutWindow" defer a transaction changing
the PIP scaling factors. This approach is we simply allow SysUI to set a pending
Transaction on a task via the WindowContainerTransaction API. The next BASE_APPLICATION
window to resize in this task will consume this transaciton, and defer the transaction
until the frame in which it resizes.

Bug: 139371701
Bug: 139371701
Test: Manual test with pending SysUI branch
Change-Id: I1e757d87e2f276db626de00685b0432508ac6d62
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index cf34b0b..f406be9 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -26,6 +26,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
+import android.view.SurfaceControl;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -77,8 +78,28 @@
     public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container,
             Rect bounds) {
         Change chg = getOrCreateChange(container.asBinder());
-        chg.mSchedulePipCallback = true;
         chg.mPinnedBounds = new Rect(bounds);
+        chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
+
+        return this;
+    }
+
+    /**
+     * Send a SurfaceControl transaction to the server, which the server will apply in sync with
+     * the next bounds change. As this uses deferred transaction and not BLAST it is only
+     * able to sync with a single window, and the first visible window in this hierarchy of type
+     * BASE_APPLICATION to resize will be used. If there are bound changes included in this
+     * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl
+     * transaction will be synced with those bounds. If there are no changes, then
+     * the SurfaceControl transaction will be synced with the next bounds change. This means
+     * that you can call this, apply the WindowContainer transaction, and then later call
+     * dismissPip() to achieve synchronization.
+     */
+    public WindowContainerTransaction setBoundsChangeTransaction(IWindowContainer container,
+            SurfaceControl.Transaction t) {
+        Change chg = getOrCreateChange(container.asBinder());
+        chg.mBoundsChangeTransaction = t;
+        chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION;
         return this;
     }
 
@@ -174,6 +195,8 @@
      */
     public static class Change implements Parcelable {
         public static final int CHANGE_FOCUSABLE = 1;
+        public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
+        public static final int CHANGE_PIP_CALLBACK = 1 << 2;
 
         private final Configuration mConfiguration = new Configuration();
         private boolean mFocusable = true;
@@ -181,8 +204,8 @@
         private @ActivityInfo.Config int mConfigSetMask = 0;
         private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
 
-        private boolean mSchedulePipCallback = false;
         private Rect mPinnedBounds = null;
+        private SurfaceControl.Transaction mBoundsChangeTransaction = null;
 
         public Change() {}
 
@@ -192,11 +215,14 @@
             mChangeMask = in.readInt();
             mConfigSetMask = in.readInt();
             mWindowSetMask = in.readInt();
-            mSchedulePipCallback = (in.readInt() != 0);
-            if (mSchedulePipCallback ) {
+            if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) {
                 mPinnedBounds = new Rect();
                 mPinnedBounds.readFromParcel(in);
             }
+            if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) {
+                mBoundsChangeTransaction =
+                    SurfaceControl.Transaction.CREATOR.createFromParcel(in);
+            }
         }
 
         public Configuration getConfiguration() {
@@ -233,6 +259,10 @@
             return mPinnedBounds;
         }
 
+        public SurfaceControl.Transaction getBoundsChangeTransaction() {
+            return mBoundsChangeTransaction;
+        }
+
         @Override
         public String toString() {
             final boolean changesBounds =
@@ -264,10 +294,12 @@
             dest.writeInt(mConfigSetMask);
             dest.writeInt(mWindowSetMask);
 
-            dest.writeInt(mSchedulePipCallback ? 1 : 0);
-            if (mSchedulePipCallback ) {
+            if (mPinnedBounds != null) {
                 mPinnedBounds.writeToParcel(dest, flags);
             }
+            if (mBoundsChangeTransaction != null) {
+                mBoundsChangeTransaction.writeToParcel(dest, flags);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 34b5c11..0733b34 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -422,6 +422,8 @@
     /** When set, will force the task to report as invisible. */
     boolean mForceHidden = false;
 
+    SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
+
     private final FindRootHelper mFindRootHelper = new FindRootHelper();
     private class FindRootHelper {
         private ActivityRecord mRoot;
@@ -3981,4 +3983,17 @@
         mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
                 this, true /* force */);
     }
+
+    /**
+     * See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
+     * transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
+     * to resize, and it will defer the transaction until that resize frame completes.
+     */
+    void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) {
+        mMainWindowSizeChangeTransaction = t;
+    }
+
+    SurfaceControl.Transaction getMainWindowSizeChangeTransaction() {
+        return mMainWindowSizeChangeTransaction;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4b13a0c..0a6a694 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -553,6 +553,12 @@
             WindowContainerTransaction.Change c) {
         int effects = sanitizeAndApplyChange(wc, c);
 
+        final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
+        if (t != null) {
+            Task tr = (Task) wc;
+            tr.setMainWindowSizeChangeTransaction(t);
+        }
+
         Rect enterPipBounds = c.getEnterPipBounds();
         if (enterPipBounds != null) {
             Task tr = (Task) wc;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9552df7..a1a9af6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -873,6 +873,14 @@
             clipRect = mTmpClipRect;
         }
 
+        if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) &&
+            (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) {
+            mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
+                    mWin.getFrameNumber());
+            SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
+            task.setMainWindowSizeChangeTransaction(null);
+        }
+
         float surfaceWidth = mSurfaceController.getWidth();
         float surfaceHeight = mSurfaceController.getHeight();