Merge changes I24d33b94,I9db3c5d1 into rvc-dev

* changes:
  System Bars animation for fixed rotation transform
  Defer animation finish from AnimationAdatpter
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 0519b80..529c4f6 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -85,4 +85,25 @@
     }
 
     void dumpDebug(ProtoOutputStream proto);
+
+    /**
+     * Gets called when the animation is about to finish and gives the client the opportunity to
+     * defer finishing the animation, i.e. it keeps the leash around until the client calls
+     * endDeferFinishCallback.
+     * <p>
+     * This has the same effect as
+     * {@link com.android.server.wm.SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)}
+     * . The later will be evaluated first and has precedence over this method if it returns true,
+     * which means that if the {@link com.android.server.wm.SurfaceAnimator.Animatable} requests to
+     * defer its finish, this method won't be called so this adapter will never have access to the
+     * finish callback. On the other hand, if the
+     * {@link com.android.server.wm.SurfaceAnimator.Animatable}, doesn't request to defer, this
+     * {@link AnimationAdapter} is responsible for ending the animation.
+     *
+     * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
+     * @return Whether the client would like to defer the animation finish.
+     */
+    default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e31eaf7..6655e92 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -499,6 +499,8 @@
      */
     ActivityRecord mFixedRotationLaunchingApp;
 
+    FixedRotationAnimationController mFixedRotationAnimationController;
+
     final FixedRotationTransitionListener mFixedRotationTransitionListener =
             new FixedRotationTransitionListener();
 
@@ -1528,6 +1530,11 @@
     }
 
     private void startFixedRotationTransform(WindowToken token, int rotation) {
+        if (mFixedRotationAnimationController == null) {
+            mFixedRotationAnimationController = new FixedRotationAnimationController(
+                    this);
+        }
+        mFixedRotationAnimationController.hide(rotation);
         mTmpConfiguration.unset();
         final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
         final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
@@ -1549,6 +1556,13 @@
         }
     }
 
+    void finishFixedRotationAnimation() {
+        if (mFixedRotationAnimationController != null
+                && mFixedRotationAnimationController.show()) {
+            mFixedRotationAnimationController = null;
+        }
+    }
+
     /**
      * Update rotation of the display.
      *
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index ebfe70c..c3f9061 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -560,6 +560,7 @@
         }, true /* traverseTopToBottom */);
         mSeamlessRotationCount = 0;
         mRotatingSeamlessly = false;
+        mDisplayContent.finishFixedRotationAnimation();
     }
 
     private void prepareSeamlessRotation() {
@@ -646,6 +647,7 @@
                     "Performing post-rotate rotation after seamless rotation");
             // Finish seamless rotation.
             mRotatingSeamlessly = false;
+            mDisplayContent.finishFixedRotationAnimation();
 
             updateRotationAndSendNewConfigIfChanged();
         }
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
new file mode 100644
index 0000000..7aca637
--- /dev/null
+++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.AnimationSpecProto.WINDOW;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
+
+import android.content.res.Configuration;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+import com.android.internal.R;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controller to fade out and in system ui when applying a fixed rotation transform to a window
+ * token.
+ *
+ * The system bars will be fade out when the fixed rotation transform starts and will be fade in
+ * once all surfaces have been rotated.
+ */
+public class FixedRotationAnimationController {
+
+    private final WindowManagerService mWmService;
+    private boolean mShowRequested = true;
+    private int mTargetRotation = Configuration.ORIENTATION_UNDEFINED;
+    private final ArrayList<WindowState> mAnimatedWindowStates = new ArrayList<>(2);
+    private final Runnable[] mDeferredFinishCallbacks;
+
+    public FixedRotationAnimationController(DisplayContent displayContent) {
+        mWmService = displayContent.mWmService;
+        addAnimatedWindow(displayContent.getDisplayPolicy().getStatusBar());
+        addAnimatedWindow(displayContent.getDisplayPolicy().getNavigationBar());
+        mDeferredFinishCallbacks = new Runnable[mAnimatedWindowStates.size()];
+    }
+
+    private void addAnimatedWindow(WindowState windowState) {
+        if (windowState != null) {
+            mAnimatedWindowStates.add(windowState);
+        }
+    }
+
+    /**
+     * Show the previously hidden {@link WindowToken} if their surfaces have already been rotated.
+     *
+     * @return True if the show animation has been started, in which case the caller no longer needs
+     * this {@link FixedRotationAnimationController}.
+     */
+    boolean show() {
+        if (!mShowRequested && readyToShow()) {
+            mShowRequested = true;
+            for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+                WindowState windowState = mAnimatedWindowStates.get(i);
+                fadeWindowToken(true, windowState.getParent(), i);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    void hide(int targetRotation) {
+        mTargetRotation = targetRotation;
+        if (mShowRequested) {
+            mShowRequested = false;
+            for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+                WindowState windowState = mAnimatedWindowStates.get(i);
+                fadeWindowToken(false /* show */, windowState.getParent(), i);
+            }
+        }
+    }
+
+    void cancel() {
+        for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+            WindowState windowState = mAnimatedWindowStates.get(i);
+            mShowRequested = true;
+            fadeWindowToken(true /* show */, windowState.getParent(), i);
+        }
+    }
+
+    private void fadeWindowToken(boolean show, WindowContainer<WindowToken> windowToken,
+            int index) {
+        Animation animation = AnimationUtils.loadAnimation(mWmService.mContext,
+                show ? R.anim.fade_in : R.anim.fade_out);
+        LocalAnimationAdapter.AnimationSpec windowAnimationSpec = createAnimationSpec(animation);
+
+        FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter(
+                windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, index);
+
+        // We deferred the end of the animation when hiding the token, so we need to end it now that
+        // it's shown again.
+        SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
+            if (mDeferredFinishCallbacks[index] != null) {
+                mDeferredFinishCallbacks[index].run();
+                mDeferredFinishCallbacks[index] = null;
+            }
+        } : null;
+        windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
+                mShowRequested, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback);
+    }
+
+    /**
+     * Check if all the mAnimatedWindowState's surfaces have been rotated to the
+     * mTargetRotation.
+     */
+    private boolean readyToShow() {
+        for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
+            WindowState windowState = mAnimatedWindowStates.get(i);
+            if (windowState.getConfiguration().windowConfiguration.getRotation()
+                    != mTargetRotation || windowState.mPendingSeamlessRotate != null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) {
+        return new LocalAnimationAdapter.AnimationSpec() {
+
+            Transformation mTransformation = new Transformation();
+
+            @Override
+            public boolean getShowWallpaper() {
+                return true;
+            }
+
+            @Override
+            public long getDuration() {
+                return animation.getDuration();
+            }
+
+            @Override
+            public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
+                    long currentPlayTime) {
+                mTransformation.clear();
+                animation.getTransformation(currentPlayTime, mTransformation);
+                t.setAlpha(leash, mTransformation.getAlpha());
+            }
+
+            @Override
+            public void dump(PrintWriter pw, String prefix) {
+                pw.print(prefix);
+                pw.println(animation);
+            }
+
+            @Override
+            public void dumpDebugInner(ProtoOutputStream proto) {
+                final long token = proto.start(WINDOW);
+                proto.write(ANIMATION, animation.toString());
+                proto.end(token);
+            }
+        };
+    }
+
+    private class FixedRotationAnimationAdapter extends LocalAnimationAdapter {
+        private final boolean mShow;
+        private final int mIndex;
+
+        FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec,
+                SurfaceAnimationRunner surfaceAnimationRunner, boolean show, int index) {
+            super(windowAnimationSpec, surfaceAnimationRunner);
+            mShow = show;
+            mIndex = index;
+        }
+
+        @Override
+        public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            // We defer the end of the hide animation to ensure the tokens stay hidden until
+            // we show them again.
+            if (!mShow) {
+                mDeferredFinishCallbacks[mIndex] = endDeferFinishCallback;
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 18e32c0..0143eb1 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -109,7 +109,10 @@
                         animationFinishCallback.onAnimationFinished(type, anim);
                     }
                 };
-                if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
+                // If both the Animatable and AnimationAdapter requests to be deferred, only the
+                // first one will be called.
+                if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
+                        || anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
                     resetAndInvokeFinish.run();
                 }
             }
@@ -486,6 +489,12 @@
     static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
 
     /**
+     * Animation when a fixed rotation transform is applied to a window token.
+     * @hide
+     */
+    static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
+
+    /**
      * Bitmask to include all animation types. This is NOT an {@link AnimationType}
      * @hide
      */
@@ -502,7 +511,8 @@
             ANIMATION_TYPE_DIMMER,
             ANIMATION_TYPE_RECENTS,
             ANIMATION_TYPE_WINDOW_ANIMATION,
-            ANIMATION_TYPE_INSETS_CONTROL
+            ANIMATION_TYPE_INSETS_CONTROL,
+            ANIMATION_TYPE_FIXED_TRANSFORM
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface AnimationType {}
@@ -592,6 +602,12 @@
          * Gets called when the animation is about to finish and gives the client the opportunity to
          * defer finishing the animation, i.e. it keeps the leash around until the client calls
          * {@link #cancelAnimation}.
+         * <p>
+         * {@link AnimationAdapter} has a similar method which is called only if this method returns
+         * false. This mean that if both this {@link Animatable} and the {@link AnimationAdapter}
+         * request to be deferred, this method is the sole responsible to call
+         * endDeferFinishCallback. On the other hand, the animation finish might still be deferred
+         * if this method return false and the one from the {@link AnimationAdapter} returns true.
          *
          * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
          * @return Whether the client would like to defer the animation finish.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 768f89e..8739bad 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -642,6 +642,9 @@
         final int originalRotation = getWindowConfiguration().getRotation();
         onConfigurationChanged(parent.getConfiguration());
         onCancelFixedRotationTransform(originalRotation);
+        if (mDisplayContent.mFixedRotationAnimationController != null) {
+            mDisplayContent.mFixedRotationAnimationController.cancel();
+        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ac95a81..7b23bfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -57,6 +57,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
@@ -1059,6 +1060,8 @@
     @Test
     public void testApplyTopFixedRotationTransform() {
         mWm.mIsFixedRotationTransformEnabled = true;
+        mDisplayContent.getDisplayPolicy().addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
+        mDisplayContent.getDisplayPolicy().addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
         final Configuration config90 = new Configuration();
         mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
 
@@ -1079,6 +1082,12 @@
                 ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
                 false /* forceUpdate */));
 
+        assertNotNull(mDisplayContent.mFixedRotationAnimationController);
+        assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
+                ANIMATION_TYPE_FIXED_TRANSFORM));
+        assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
+                ANIMATION_TYPE_FIXED_TRANSFORM));
+
         final Rect outFrame = new Rect();
         final Rect outInsets = new Rect();
         final Rect outStableInsets = new Rect();
@@ -1131,6 +1140,9 @@
         assertFalse(app.hasFixedRotationTransform());
         assertFalse(app2.hasFixedRotationTransform());
         assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
+
+        mDisplayContent.finishFixedRotationAnimation();
+        assertNull(mDisplayContent.mFixedRotationAnimationController);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 552c476..79ba175 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 
 import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceControl.Transaction;
@@ -52,6 +53,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.PrintWriter;
+
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
@@ -267,6 +270,27 @@
         assertFalse(mDeferFinishAnimatable.mFinishedCallbackCalled);
     }
 
+    @Test
+    public void testDeferFinishFromAdapter() {
+
+        DeferredFinishAdapter deferredFinishAdapter = new DeferredFinishAdapter();
+        // Start animation
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, deferredFinishAdapter,
+                true /* hidden */,
+                ANIMATION_TYPE_APP_TRANSITION);
+        assertAnimating(mAnimatable);
+        deferredFinishAdapter.mFinishCallback.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
+                deferredFinishAdapter);
+
+        assertAnimating(mAnimatable);
+        assertFalse(mAnimatable.mFinishedCallbackCalled);
+        // Now end defer finishing.
+        deferredFinishAdapter.mEndDeferFinishCallback.run();
+        assertNotAnimating(mAnimatable);
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        verify(mTransaction).remove(eq(deferredFinishAdapter.mAnimationLeash));
+    }
+
     private OnAnimationFinishedCallback startDeferFinishAnimatable(AnimationAdapter anim) {
         mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, anim,
                 true /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -389,4 +413,51 @@
             return true;
         }
     }
+
+    private static class DeferredFinishAdapter implements AnimationAdapter {
+
+        private Runnable mEndDeferFinishCallback;
+        private OnAnimationFinishedCallback mFinishCallback;
+        private SurfaceControl mAnimationLeash;
+
+        @Override
+        public boolean getShowWallpaper() {
+            return true;
+        }
+
+        @Override
+        public void startAnimation(SurfaceControl animationLeash, Transaction t, int type,
+                OnAnimationFinishedCallback finishCallback) {
+            mFinishCallback = finishCallback;
+            mAnimationLeash = animationLeash;
+        }
+
+        @Override
+        public void onAnimationCancelled(SurfaceControl animationLeash) {
+        }
+
+        @Override
+        public long getDurationHint() {
+            return 100;
+        }
+
+        @Override
+        public long getStatusBarTransitionsStartTime() {
+            return 100;
+        }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+        }
+
+        @Override
+        public void dumpDebug(ProtoOutputStream proto) {
+        }
+
+        @Override
+        public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            mEndDeferFinishCallback = endDeferFinishCallback;
+            return true;
+        }
+    }
 }