Delay PiP transition to fullscreen until activities draw.

To avoid awful stretching.

Bug: 37473110
Test: Transition app fullscreen verify no awful video stretching.
Change-Id: I810a72207e45b8f83a63c9f0b3cc9a433569852c
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1fb34eb..7634644 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1280,6 +1280,11 @@
                 // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
                 dc.setLayoutNeeded();
                 mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+
+                final TaskStack s = getStack();
+                if (s != null) {
+                    s.onAllWindowsDrawn();
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index e634552..7f3c89c 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -112,6 +112,8 @@
     private final Interpolator mFastOutSlowInInterpolator;
     private boolean mFinishAnimationAfterTransition = false;
 
+    private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
+
     BoundsAnimationController(Context context, AppTransition transition, Handler handler) {
         mHandler = handler;
         mAppTransition = transition;
@@ -175,6 +177,13 @@
             }
         }
 
+        final Runnable mResumeRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    resume();
+                }
+        };
+
         @Override
         public void onAnimationStart(Animator animation) {
             if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
@@ -196,10 +205,26 @@
             // the starting position so we don't jump at the beginning of the animation.
             if (animatingToLargerSize()) {
                 mTarget.setPinnedStackSize(mFrom, mTmpRect);
+
+                // We pause the animation until the app has drawn at the new size.
+                // The target will notify us via BoundsAnimationController#resume.
+                // We do this here and pause the animation, rather than just defer starting it
+                // so we can enter the animating state and have WindowStateAnimator apply the
+                // correct logic to make this resize seamless.
+                if (mMoveToFullscreen) {
+                    pause();
+                    mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
+                }
             }
         }
 
         @Override
+        public void resume() {
+            mHandler.removeCallbacks(mResumeRunnable);
+            super.resume();
+        }
+
+        @Override
         public void onAnimationUpdate(ValueAnimator animation) {
             final float value = (Float) animation.getAnimatedValue();
             final float remains = 1 - value;
@@ -371,4 +396,15 @@
         animator.start();
         return animator;
     }
+
+    private void resume() {
+        for (int i = 0; i < mRunningAnimations.size(); i++) {
+            final BoundsAnimator b = mRunningAnimations.valueAt(i);
+            b.resume();
+        }
+    }
+
+    public void onAllWindowsDrawn() {
+        mHandler.post(this::resume);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index d141f7c..1feb743 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -52,6 +52,7 @@
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
+import com.android.server.UiThread;
 
 import java.io.PrintWriter;
 
@@ -1475,6 +1476,14 @@
         return true;
     }
 
+    void onAllWindowsDrawn() {
+        if (!mBoundsAnimating) {
+            return;
+        }
+
+        mService.mBoundsAnimationController.onAllWindowsDrawn();
+    }
+
     @Override  // AnimatesBounds
     public void onAnimationStart(boolean schedulePipModeChangedCallback) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
@@ -1482,6 +1491,13 @@
             mBoundsAnimatingRequested = false;
             mBoundsAnimating = true;
             mCancelCurrentBoundsAnimation = false;
+
+            // If we are changing UI mode, as in the PiP to fullscreen
+            // transition, then we need to wait for the window to draw.
+            if (schedulePipModeChangedCallback) {
+                forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); },
+                        false /* traverseTopToBottom */);
+            }
         }
 
         if (mStackId == PINNED_STACK_ID) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9555c8d..b9776a3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4329,7 +4329,9 @@
 
     int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges,
             int oldVisibility) {
-        result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0;
+        final boolean wasVisible = isVisibleLw();
+
+        result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
         if (mAnimatingExit) {
             Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
                     + mRemoveOnExit + ", mDestroying=" + mDestroying);
@@ -4348,7 +4350,7 @@
         mLastVisibleLayoutRotation = getDisplayContent().getRotation();
 
         mWinAnimator.mEnteringAnimation = true;
-        if ((result & RELAYOUT_RES_FIRST_TIME) != 0) {
+        if (!wasVisible) {
             prepareWindowToDisplayDuringRelayout(mergedConfiguration);
         }
         if ((attrChanges & FORMAT_CHANGED) != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a2889b1..b945cf1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -602,6 +602,22 @@
         }
     }
 
+    void resetDrawState() {
+        mDrawState = DRAW_PENDING;
+
+        if (mWin.mAppToken == null) {
+            return;
+        }
+
+        if (mWin.mAppToken.mAppAnimator.animation == null) {
+            mWin.mAppToken.clearAllDrawn();
+        } else {
+            // Currently animating, persist current state of allDrawn until animation
+            // is complete.
+            mWin.mAppToken.deferClearAllDrawn = true;
+        }
+    }
+
     WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
         final WindowState w = mWin;
         if (w.restoreSavedSurface()) {
@@ -619,16 +635,7 @@
         if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
                 "createSurface " + this + ": mDrawState=DRAW_PENDING");
 
-        mDrawState = DRAW_PENDING;
-        if (w.mAppToken != null) {
-            if (w.mAppToken.mAppAnimator.animation == null) {
-                w.mAppToken.clearAllDrawn();
-            } else {
-                // Currently animating, persist current state of allDrawn until animation
-                // is complete.
-                w.mAppToken.deferClearAllDrawn = true;
-            }
-        }
+        resetDrawState();
 
         mService.makeWindowFreezingScreenIfNeededLocked(w);