Move PiP animation to SysUI package

The bounds animation is cleaned up within window manager and it's now
the SysUI component listening on callbacks from TaskOrganizer for
entering to and exiting from PiP mode.

Additionally, the expand and move of the PiP window is now part of SysUI
as well.

Known issues:
- Black background when in transition from PiP to fullscreen. The
wallpaper gets into hidden state too early
- App gets into PiP mode too early when entering PiP, need to defer the
configuration change sent to app in this case

Bug: 146594635
Bug: 148198539
Bug: 138144750
Bug: 149569903
Test: atest PinnedStackTests
Test: atest PipAnimationControllerTest
Test: atest RecentsAnimationTest
Test: atest RecentTasksTest
Test: atest com.android.server.wm.ActivityStarterTests
Merged-In: Id0c8ce03fa26952daf5e3687b18b2eb2375b7d20
Change-Id: Id0c8ce03fa26952daf5e3687b18b2eb2375b7d20
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d15c0e..d32c77f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,7 +118,6 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 9ba56cf..d48b35b 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -362,25 +362,6 @@
     }
 
     /**
-     * Resize the input stack id to the given bounds with animate setting.
-     * @param stackId Id of the stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param animate Whether we should play an animation for resizing stack.
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizePinnedStack(int stackId, Rect bounds, boolean animate) {
-        try {
-            if (animate) {
-                getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */);
-            } else {
-                getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Resize task to given bounds.
      * @param taskId Id of task to resize.
      * @param bounds Bounds to resize task.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 5b61402..4e3aa7d 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -236,33 +236,9 @@
      */
     boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
-    /**
-     * Resizes the input pinned stack to the given bounds with animation.
-     *
-     * @param stackId Id of the pinned stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     * @throws RemoteException
-     */
-    void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
             boolean animate, in Rect initialBounds, boolean showRecents);
     /**
-     * Use the offset to adjust the stack boundary with animation.
-     *
-     * @param stackId Id of the stack to adjust.
-     * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to
-     *                      the compareBounds.
-     * @param xOffset The horizontal offset.
-     * @param yOffset The vertical offset.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     * @throws RemoteException
-     */
-    void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset,
-            int animationDuration);
-    /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
@@ -371,23 +347,10 @@
     void startLocalVoiceInteraction(in IBinder token, in Bundle options);
     void stopLocalVoiceInteraction(in IBinder token);
     boolean supportsLocalVoiceInteraction();
-    void notifyPinnedStackAnimationStarted();
-    void notifyPinnedStackAnimationEnded();
 
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
-    /**
-     * Resizes the pinned stack.
-     *
-     * @param pinnedBounds The bounds for the pinned stack.
-     * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
-     *                             might be different from the stack bounds to allow more
-     *                             flexibility while resizing, or {@code null} if they should be the
-     *                             same as the stack bounds.
-     */
-    void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds);
-
     void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
             in CharSequence message);
 
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 37bdda0..28b28da 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -46,16 +46,6 @@
     void onPinnedActivityRestartAttempt(boolean clearedTask);
 
     /**
-     * Called whenever the pinned stack is starting animating a resize.
-     */
-    void onPinnedStackAnimationStarted();
-
-    /**
-     * Called whenever the pinned stack is done animating a resize.
-     */
-    void onPinnedStackAnimationEnded();
-
-    /**
      * Called when we launched an activity that we forced to be resizable.
      *
      * @param packageName Package name of the top activity in the task.
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index da0aadb..b892b8e 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -58,16 +58,6 @@
 
     @Override
     @UnsupportedAppUsage
-    public void onPinnedStackAnimationStarted() throws RemoteException {
-    }
-
-    @Override
-    @UnsupportedAppUsage
-    public void onPinnedStackAnimationEnded() throws RemoteException {
-    }
-
-    @Override
-    @UnsupportedAppUsage
     public void onActivityForcedResizable(String packageName, int taskId, int reason)
             throws RemoteException {
     }
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index cb82f16..1cf83a3 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -25,32 +25,8 @@
  * @hide
  */
 interface IPinnedStackController {
-
-    /**
-     * Notifies the controller that the PiP is currently minimized.
-     */
-    oneway void setIsMinimized(boolean isMinimized);
-
     /**
      * @return what WM considers to be the current device rotation.
      */
     int getDisplayRotation();
-
-    /**
-     * Notifies the controller to actually start the PiP animation.
-     * The bounds would be calculated based on the last save reentry fraction internally.
-     * {@param destinationBounds} is the stack bounds of the final PiP window
-     * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture,
-     * expect the same bound passed via IPinnedStackListener#onPrepareAnimation.
-     * {@param animationDuration} suggests the animation duration transitioning to PiP window.
-     */
-    void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration);
-
-    /**
-     * Notifies the controller to reset on bounds animation, if there is any.
-     * This could happen when screen rotation is happening and we need to notify the WM to reset
-     * any running bounds animation on the pinned stack.
-     * {@param bounds} here is the final destination bounds.
-     */
-    void resetBoundsAnimation(in Rect bounds);
 }
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index d01c933..596d55a 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -43,8 +43,7 @@
      * pinned stack (the final bounds if animating, the current bounds if not),
      * which may be helpful in calculating dependent animation bounds.
      */
-    void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment,
-            boolean fromShelfAdjustment);
+    void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment);
 
     /**
      * Called when window manager decides to adjust the pinned stack bounds because of the IME, or
@@ -55,12 +54,6 @@
     void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
 
     /**
-     * Called when window manager decides to adjust the minimized state, or when the listener
-     * is first registered to allow the listener to synchronized its state with the controller.
-     */
-    void onMinimizedStateChanged(boolean isMinimized);
-
-    /**
      * Called when the set of actions for the current PiP activity changes, or when the listener
      * is first registered to allow the listener to synchronized its state with the controller.
      */
@@ -99,13 +92,4 @@
      * Called by the window manager when the aspect ratio is reset.
      */
     void onAspectRatioChanged(float aspectRatio);
-
-    /**
-     * Called by the window manager to notify the listener to prepare for PiP animation.
-     * Internally, the target bounds would be calculated from the given {@param aspectRatio}
-     * and {@param bounds}, the saved reentry snap fraction also contributes.
-     * Caller would wait for a IPinnedStackController#startAnimation callback to actually
-     * start the animation, see details in IPinnedStackController.
-     */
-    void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds);
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 762366e..530dffb 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -77,13 +77,6 @@
     void hideCurrentInputMethod();
 
     /**
-     * This call is deprecated, use #setDeferCancelUntilNextTransition() instead
-     * TODO(138144750): Remove this method once there are no callers
-     * @deprecated
-     */
-    void setCancelWithDeferredScreenshot(boolean screenshot);
-
-    /**
      * Clean up the screenshot of previous task which was created during recents animation that
      * was cancelled by a stack order change.
      *
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
new file mode 100644
index 0000000..97aa512
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.systemui.shared.recents;
+
+/**
+ * Listener interface that Launcher attaches to SystemUI to get
+ * pinned stack animation callbacks.
+ */
+oneway interface IPinnedStackAnimationListener {
+    /**
+     * Notifies the pinned stack animation is started.
+     */
+    void onPinnedStackAnimationStarted();
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index b1d39f5..80fd826 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -22,9 +22,11 @@
 import android.os.Bundle;
 import android.view.MotionEvent;
 
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
 /**
  * Temporary callbacks into SystemUI.
- * Next id = 22
+ * Next id = 25
  */
 interface ISystemUiProxy {
 
@@ -121,11 +123,21 @@
     /**
      * Handle the provided image as if it was a screenshot.
      */
-     void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
+    void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
               in Insets visibleInsets, int taskId) = 21;
 
     /**
      * Sets the split-screen divider minimized state
      */
     void setSplitScreenMinimized(boolean minimized) = 22;
+
+    /*
+     * Notifies that the swipe-to-home (recents animation) is finished.
+     */
+    void notifySwipeToHomeFinished() = 23;
+
+    /**
+     * Sets listener to get pinned stack animation callbacks.
+     */
+    void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 8c0ffb8..360244c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -53,11 +53,9 @@
     }
 
     @Override
-    public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-            boolean fromShelfAdjustment) {
+    public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
         for (PinnedStackListener listener : mListeners) {
-            listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment,
-                    fromShelfAdjustment);
+            listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
         }
     }
 
@@ -69,13 +67,6 @@
     }
 
     @Override
-    public void onMinimizedStateChanged(boolean isMinimized) {
-        for (PinnedStackListener listener : mListeners) {
-            listener.onMinimizedStateChanged(isMinimized);
-        }
-    }
-
-    @Override
     public void onActionsChanged(ParceledListSlice actions) {
         for (PinnedStackListener listener : mListeners) {
             listener.onActionsChanged(actions);
@@ -117,13 +108,6 @@
         }
     }
 
-    @Override
-    public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
-        for (PinnedStackListener listener : mListeners) {
-            listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
-        }
-    }
-
     /**
      * A counterpart of {@link IPinnedStackListener} with empty implementations.
      * Subclasses can ignore those methods they do not intend to take action upon.
@@ -131,13 +115,10 @@
     public static class PinnedStackListener {
         public void onListenerRegistered(IPinnedStackController controller) {}
 
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {}
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {}
 
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
 
-        public void onMinimizedStateChanged(boolean isMinimized) {}
-
         public void onActionsChanged(ParceledListSlice actions) {}
 
         public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {}
@@ -149,7 +130,5 @@
         public void onConfigurationChanged() {}
 
         public void onAspectRatioChanged(float aspectRatio) {}
-
-        public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {}
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 748f356..6cd6118 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -91,15 +91,6 @@
         }
     }
 
-    @Deprecated
-    public void setCancelWithDeferredScreenshot(boolean screenshot) {
-        try {
-            mAnimationController.setCancelWithDeferredScreenshot(screenshot);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to set cancel with deferred screenshot", e);
-        }
-    }
-
     public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
         try {
             mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 5f92b28..1c6223b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -20,8 +20,6 @@
 import android.app.ITaskStackListener;
 import android.content.ComponentName;
 import android.os.IBinder;
-import android.os.UserHandle;
-import android.util.Log;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -40,8 +38,6 @@
     public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
     public void onActivityUnpinned() { }
     public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
-    public void onPinnedStackAnimationStarted() { }
-    public void onPinnedStackAnimationEnded() { }
     public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
     public void onActivityDismissingDockedStack() { }
     public void onActivityLaunchOnSecondaryDisplayFailed() { }
@@ -117,22 +113,4 @@
 
     /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
     public void onRecentTaskListFrozenChanged(boolean frozen) { }
-
-    /**
-     * Checks that the current user matches the process. Since
-     * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
-     * {@link TaskStackChangeListener} should make this call to verify that we don't act on events
-     * originating from another user's interactions.
-     */
-    protected final boolean checkCurrentUserId(int currentUserId, boolean debug) {
-        int processUserId = UserHandle.myUserId();
-        if (processUserId != currentUserId) {
-            if (debug) {
-                Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId
-                        + " and the current user is uid=" + currentUserId);
-            }
-            return false;
-        }
-        return true;
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index acce41c..cbdd3f8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -128,18 +128,6 @@
     }
 
     @Override
-    public void onPinnedStackAnimationStarted() throws RemoteException {
-        mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
-        mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
-    }
-
-    @Override
-    public void onPinnedStackAnimationEnded() throws RemoteException {
-        mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
-        mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
-    }
-
-    @Override
     public void onActivityForcedResizable(String packageName, int taskId, int reason)
             throws RemoteException {
         mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
@@ -249,11 +237,9 @@
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
         private static final int ON_ACTIVITY_PINNED = 3;
         private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
-        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
         private static final int ON_TASK_PROFILE_LOCKED = 8;
-        private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
         private static final int ON_ACTIVITY_UNPINNED = 10;
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
         private static final int ON_TASK_CREATED = 12;
@@ -317,18 +303,6 @@
                         }
                         break;
                     }
-                    case ON_PINNED_STACK_ANIMATION_STARTED: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
-                        }
-                        break;
-                    }
-                    case ON_PINNED_STACK_ANIMATION_ENDED: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
-                        }
-                        break;
-                    }
                     case ON_ACTIVITY_FORCED_RESIZABLE: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onActivityForcedResizable(
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index adee7f2..38744fe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -18,6 +18,8 @@
 
 import android.content.res.Configuration;
 
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
 import java.io.PrintWriter;
 
 
@@ -27,5 +29,7 @@
     default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
     void onConfigurationChanged(Configuration newConfig);
     default void setShelfHeight(boolean visible, int height) {}
+    default void setPinnedStackAnimationType(int animationType) {}
+    default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {}
     default void dump(PrintWriter pw) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
new file mode 100644
index 0000000..b5fd406
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -0,0 +1,317 @@
+/*
+ * 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.systemui.pip;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller class of PiP animations (both from and to PiP mode).
+ */
+public class PipAnimationController {
+    private static final float FRACTION_START = 0f;
+    private static final float FRACTION_END = 1f;
+
+    public static final int DURATION_NONE = 0;
+    public static final int DURATION_DEFAULT_MS = 425;
+    public static final int ANIM_TYPE_BOUNDS = 0;
+    public static final int ANIM_TYPE_ALPHA = 1;
+
+    @IntDef(prefix = { "ANIM_TYPE_" }, value = {
+            ANIM_TYPE_BOUNDS,
+            ANIM_TYPE_ALPHA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AnimationType {}
+
+    private final Interpolator mFastOutSlowInInterpolator;
+
+    private PipTransitionAnimator mCurrentAnimator;
+
+    PipAnimationController(Context context) {
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+    }
+
+    @MainThread
+    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect destinationBounds, float alphaStart, float alphaEnd) {
+        if (mCurrentAnimator == null) {
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+                            destinationBounds, alphaStart, alphaEnd));
+        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
+                && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.updateEndValue(alphaEnd);
+        } else {
+            mCurrentAnimator.cancel();
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+                            destinationBounds, alphaStart, alphaEnd));
+        }
+        return mCurrentAnimator;
+    }
+
+    @MainThread
+    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect startBounds, Rect endBounds) {
+        if (mCurrentAnimator == null) {
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
+                && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.setDestinationBounds(endBounds);
+            // construct new Rect instances in case they are recycled
+            mCurrentAnimator.updateEndValue(new Rect(endBounds));
+        } else {
+            mCurrentAnimator.cancel();
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+        }
+        return mCurrentAnimator;
+    }
+
+    private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setFloatValues(FRACTION_START, FRACTION_END);
+        return animator;
+    }
+
+    /**
+     * Additional callback interface for PiP animation
+     */
+    public static class PipAnimationCallback {
+        /**
+         * Called when PiP animation is started.
+         */
+        public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {}
+
+        /**
+         * Called when PiP animation is ended.
+         */
+        public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+                PipTransitionAnimator animator) {}
+
+        /**
+         * Called when PiP animation is cancelled.
+         */
+        public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {}
+    }
+
+    /**
+     * Animator for PiP transition animation which supports both alpha and bounds animation.
+     * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
+     */
+    public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
+            ValueAnimator.AnimatorUpdateListener,
+            ValueAnimator.AnimatorListener {
+        private final IWindowContainer mWindowContainer;
+        private final boolean mScheduleFinishPip;
+        private final SurfaceControl mLeash;
+        private final @AnimationType int mAnimationType;
+        private final Rect mDestinationBounds = new Rect();
+
+        private T mStartValue;
+        private T mEndValue;
+        private T mCurrentValue;
+        private PipAnimationCallback mPipAnimationCallback;
+        private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
+
+        private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+                @AnimationType int animationType, Rect destinationBounds,
+                T startValue, T endValue) {
+            mWindowContainer = wc;
+            mScheduleFinishPip = scheduleFinishPip;
+            try {
+                mLeash = wc.getLeash();
+                mAnimationType = animationType;
+                mDestinationBounds.set(destinationBounds);
+                mStartValue = startValue;
+                mEndValue = endValue;
+                addListener(this);
+                addUpdateListener(this);
+                mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mCurrentValue = mStartValue;
+            applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this);
+            }
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
+                    animation.getAnimatedFraction());
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mCurrentValue = mEndValue;
+            final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+            applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this);
+            }
+        }
+
+        @Override public void onAnimationRepeat(Animator animation) {}
+
+        @AnimationType int getAnimationType() {
+            return mAnimationType;
+        }
+
+        PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
+            mPipAnimationCallback = callback;
+            return this;
+        }
+
+        boolean shouldScheduleFinishPip() {
+            return mScheduleFinishPip;
+        }
+
+        T getStartValue() {
+            return mStartValue;
+        }
+
+        T getEndValue() {
+            return mEndValue;
+        }
+
+        Rect getDestinationBounds() {
+            return mDestinationBounds;
+        }
+
+        void setDestinationBounds(Rect destinationBounds) {
+            mDestinationBounds.set(destinationBounds);
+        }
+
+        void setCurrentValue(T value) {
+            mCurrentValue = value;
+        }
+
+        /**
+         * Updates the {@link #mEndValue}.
+         *
+         * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
+         * This is typically used when we receive a shelf height adjustment during the bounds
+         * animation. In which case we can update the end bounds and keep the existing animation
+         * running instead of cancelling it.
+         */
+        void updateEndValue(T endValue) {
+            mEndValue = endValue;
+            mStartValue = mCurrentValue;
+        }
+
+        SurfaceControl.Transaction newSurfaceControlTransaction() {
+            return mSurfaceControlTransactionFactory.getTransaction();
+        }
+
+        @VisibleForTesting
+        void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
+            mSurfaceControlTransactionFactory = factory;
+        }
+
+        abstract void applySurfaceControlTransaction(SurfaceControl leash,
+                SurfaceControl.Transaction tx, float fraction);
+
+        static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip,
+                Rect destinationBounds, float startValue, float endValue) {
+            return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA,
+                    destinationBounds, startValue, endValue) {
+                @Override
+                void applySurfaceControlTransaction(SurfaceControl leash,
+                        SurfaceControl.Transaction tx, float fraction) {
+                    final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
+                    setCurrentValue(alpha);
+                    tx.setAlpha(leash, alpha);
+                    if (Float.compare(fraction, FRACTION_START) == 0) {
+                        // Ensure the start condition
+                        final Rect bounds = getDestinationBounds();
+                        tx.setPosition(leash, bounds.left, bounds.top)
+                                .setWindowCrop(leash, bounds.width(), bounds.height());
+                    }
+                    tx.apply();
+                }
+            };
+        }
+
+        static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip,
+                Rect startValue, Rect endValue) {
+            // construct new Rect instances in case they are recycled
+            return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS,
+                    endValue, new Rect(startValue), new Rect(endValue)) {
+                private final Rect mTmpRect = new Rect();
+
+                private int getCastedFractionValue(float start, float end, float fraction) {
+                    return (int) (start * (1 - fraction) + end * fraction + .5f);
+                }
+
+                @Override
+                void applySurfaceControlTransaction(SurfaceControl leash,
+                        SurfaceControl.Transaction tx, float fraction) {
+                    final Rect start = getStartValue();
+                    final Rect end = getEndValue();
+                    mTmpRect.set(
+                            getCastedFractionValue(start.left, end.left, fraction),
+                            getCastedFractionValue(start.top, end.top, fraction),
+                            getCastedFractionValue(start.right, end.right, fraction),
+                            getCastedFractionValue(start.bottom, end.bottom, fraction));
+                    setCurrentValue(mTmpRect);
+                    tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
+                            .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
+                    if (Float.compare(fraction, FRACTION_START) == 0) {
+                        // Ensure the start condition
+                        tx.setAlpha(leash, 1f);
+                    }
+                    tx.apply();
+                }
+            };
+        }
+    }
+
+    interface SurfaceControlTransactionFactory {
+        SurfaceControl.Transaction getTransaction();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 41b3130..8c3ccaa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -36,7 +36,6 @@
 import android.util.TypedValue;
 import android.view.DisplayInfo;
 import android.view.Gravity;
-import android.view.IPinnedStackController;
 import android.view.IWindowManager;
 import android.view.WindowContainerTransaction;
 import android.view.WindowManagerGlobal;
@@ -56,9 +55,7 @@
     private final IWindowManager mWindowManager;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
-    private final Rect mStableInsets = new Rect();
     private final Rect mTmpInsets = new Rect();
-    private final Point mTmpDisplaySize = new Point();
 
     /**
      * Tracks the destination bounds, used for any following
@@ -66,7 +63,6 @@
      */
     private final Rect mLastDestinationBounds = new Rect();
 
-    private IPinnedStackController mPinnedStackController;
     private ComponentName mLastPipComponentName;
     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
     private Size mReentrySize = null;
@@ -80,7 +76,6 @@
     private Point mScreenEdgeInsets;
     private int mCurrentMinSize;
 
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private boolean mIsShelfShowing;
@@ -123,10 +118,6 @@
                 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
     }
 
-    public void setPinnedStackController(IPinnedStackController controller) {
-        mPinnedStackController = controller;
-    }
-
     public void setMinEdgeSize(int minEdgeSize) {
         mCurrentMinSize = minEdgeSize;
     }
@@ -155,14 +146,6 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on minimized state change.
-     */
-    public void onMinimizedStateChanged(boolean minimized) {
-        mIsMinimized = minimized;
-        mSnapAlgorithm.setMinimized(minimized);
-    }
-
-    /**
      * Responds to IPinnedStackListener on movement bounds change.
      * Note that both inset and normal bounds will be calculated here rather than in the caller.
      */
@@ -238,9 +221,9 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on preparing the pinned stack animation.
+     * @return {@link Rect} of the destination PiP window bounds.
      */
-    public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+    Rect getDestinationBounds(float aspectRatio, Rect bounds) {
         final Rect destinationBounds;
         final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
         if (bounds == null) {
@@ -253,17 +236,16 @@
                     false /* useCurrentMinEdgeSize */);
         }
         if (destinationBounds.equals(bounds)) {
-            return;
+            return bounds;
         }
         mAspectRatio = aspectRatio;
         onResetReentryBoundsUnchecked();
-        try {
-            mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
-                    -1 /* animationDuration */);
-            mLastDestinationBounds.set(destinationBounds);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to start PiP animation from SysUI", e);
-        }
+        mLastDestinationBounds.set(destinationBounds);
+        return destinationBounds;
+    }
+
+    float getDefaultAspectRatio() {
+        return mDefaultAspectRatio;
     }
 
     /**
@@ -307,18 +289,10 @@
                 false /* adjustForIme */);
         mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                 snapFraction);
-        if (mIsMinimized) {
-            applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
-        }
 
-        try {
-            outBounds.set(postChangeStackBounds);
-            mLastDestinationBounds.set(outBounds);
-            mPinnedStackController.resetBoundsAnimation(outBounds);
-            t.setBounds(pinnedStackInfo.stackToken, outBounds);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to resize PiP on display rotation", e);
-        }
+        outBounds.set(postChangeStackBounds);
+        mLastDestinationBounds.set(outBounds);
+        t.setBounds(pinnedStackInfo.stackToken, outBounds);
         return true;
     }
 
@@ -370,9 +344,6 @@
         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
         mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
-        if (mIsMinimized) {
-            applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
-        }
     }
 
     /**
@@ -436,20 +407,6 @@
     }
 
     /**
-     * Applies the minimized offsets to the given stack bounds.
-     */
-    private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
-        mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-        try {
-            mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets);
-            mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
-                    mStableInsets);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get stable insets from WM", e);
-        }
-    }
-
-    /**
      * @return the default snap fraction to apply instead of the default gravity when calculating
      *         the default stack bounds when first entering PiP.
      */
@@ -486,7 +443,6 @@
         pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
         pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
index f3e707c..6b89718 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
@@ -63,14 +63,9 @@
 
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
-    private final int mMinimizedVisibleSize;
-    private boolean mIsMinimized;
-
     public PipSnapAlgorithm(Context context) {
         Resources res = context.getResources();
         mContext = context;
-        mMinimizedVisibleSize = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.pip_minimized_visible_size);
         mDefaultSizePercent = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
         mMaxAspectRatioForMinSize = res.getFloat(
@@ -92,13 +87,6 @@
     }
 
     /**
-     * Sets the PIP's minimized state.
-     */
-    public void setMinimized(boolean isMinimized) {
-        mIsMinimized = isMinimized;
-    }
-
-    /**
      * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
      * the given {@param velocityX} and {@param velocityY}.  The {@param movementBounds} should be
      * those for the given {@param stackBounds}.
@@ -235,20 +223,6 @@
     }
 
     /**
-     * Applies the offset to the {@param stackBounds} to adjust it to a minimized state.
-     */
-    public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize,
-            Rect stableInsets) {
-        if (stackBounds.left <= movementBounds.centerX()) {
-            stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(),
-                    stackBounds.top);
-        } else {
-            stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize,
-                    stackBounds.top);
-        }
-    }
-
-    /**
      * @return returns a fraction that describes where along the {@param movementBounds} the
      *         {@param stackBounds} are. If the {@param stackBounds} are not currently on the
      *         {@param movementBounds} exactly, then they will be snapped to the movement bounds.
@@ -402,16 +376,11 @@
      * the new bounds out to {@param boundsOut}.
      */
     private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) {
-        // If the stackBounds are minimized, then it should only be snapped back horizontally
         final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right,
                 stackBounds.left));
         final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
                 stackBounds.top));
         boundsOut.set(stackBounds);
-        if (mIsMinimized) {
-            boundsOut.offsetTo(boundedLeft, boundedTop);
-            return;
-        }
 
         // Otherwise, just find the closest edge
         final int fromLeft = Math.abs(stackBounds.left - movementBounds.left);
@@ -479,7 +448,5 @@
         pw.println(prefix + PipSnapAlgorithm.class.getSimpleName());
         pw.println(innerPrefix + "mSnapMode=" + mSnapMode);
         pw.println(innerPrefix + "mOrientation=" + mOrientation);
-        pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
new file mode 100644
index 0000000..1555153
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -0,0 +1,291 @@
+/*
+ * 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.systemui.pip;
+
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
+import static com.android.systemui.pip.PipAnimationController.DURATION_NONE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ITaskOrganizerController;
+import android.app.PictureInPictureParams;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.DisplayInfo;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.WindowContainerTransaction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages PiP tasks such as resize and offset.
+ *
+ * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change
+ * both to and from PiP and issues corresponding animation if applicable.
+ * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running
+ * and files a final {@link WindowContainerTransaction} at the end of the transition.
+ *
+ * This class is also responsible for general resize/offset PiP operations within SysUI component,
+ * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
+ */
+public class PipTaskOrganizer extends ITaskOrganizer.Stub {
+    private static final String TAG = PipTaskOrganizer.class.getSimpleName();
+
+    private final Handler mMainHandler;
+    private final ITaskOrganizerController mTaskOrganizerController;
+    private final PipBoundsHandler mPipBoundsHandler;
+    private final PipAnimationController mPipAnimationController;
+    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+    private final Rect mDisplayBounds = new Rect();
+    private final Rect mLastReportedBounds = new Rect();
+
+    private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
+            new PipAnimationController.PipAnimationCallback() {
+        @Override
+        public void onPipAnimationStart(IWindowContainer wc,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionStarted();
+                }
+            });
+        }
+
+        @Override
+        public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionFinished();
+                }
+            });
+            final Rect destinationBounds = animator.getDestinationBounds();
+            mLastReportedBounds.set(destinationBounds);
+            try {
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                if (animator.shouldScheduleFinishPip()) {
+                    wct.scheduleFinishEnterPip(wc, destinationBounds);
+                } else {
+                    wct.setBounds(wc, destinationBounds);
+                }
+                wct.setBoundsChangeTransaction(wc, tx);
+                mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to apply container transaction", e);
+            }
+        }
+
+        @Override
+        public void onPipAnimationCancel(IWindowContainer wc,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionCanceled();
+                }
+            });
+        }
+    };
+
+    private ActivityManager.RunningTaskInfo mTaskInfo;
+    private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+    public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
+        mPipBoundsHandler = boundsHandler;
+        mPipAnimationController = new PipAnimationController(context);
+    }
+
+    /**
+     * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+     */
+    public void resizePinnedStack(Rect destinationBounds, int durationMs) {
+        Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
+        resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */,
+                mLastReportedBounds, destinationBounds, durationMs);
+    }
+
+    /**
+     * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+     */
+    public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
+        if (mTaskInfo == null) {
+            Log.w(TAG, "mTaskInfo is not set");
+            return;
+        }
+        final Rect destinationBounds = new Rect(originalBounds);
+        destinationBounds.offset(xOffset, yOffset);
+        resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
+                originalBounds, destinationBounds, durationMs);
+    }
+
+    /**
+     * Registers {@link PipTransitionCallback} to receive transition callbacks.
+     */
+    public void registerPipTransitionCallback(PipTransitionCallback callback) {
+        mPipTransitionCallbacks.add(callback);
+    }
+
+    /**
+     * Sets the preferred animation type for one time.
+     * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}.
+     */
+    public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+        mOneShotAnimationType = animationType;
+    }
+
+    /**
+     * Updates the display dimension with given {@link DisplayInfo}
+     */
+    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+    }
+
+    /**
+     * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
+     * @param destinationBounds the final bounds.
+     */
+    public void onMotionMovementEnd(Rect destinationBounds) {
+        try {
+            mLastReportedBounds.set(destinationBounds);
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setBounds(mTaskInfo.token, destinationBounds);
+            mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to apply window container transaction", e);
+        }
+    }
+
+    @Override
+    public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+        Objects.requireNonNull(info, "Requires RunningTaskInfo");
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+        mTaskInfo = info;
+        if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+            final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+            resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */,
+                    currentBounds, destinationBounds,
+                    PipAnimationController.DURATION_DEFAULT_MS);
+        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+            mMainHandler.post(() -> mPipAnimationController
+                    .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
+                            destinationBounds, 0f, 1f)
+                    .setPipAnimationCallback(mPipAnimationCallback)
+                    .setDuration(PipAnimationController.DURATION_DEFAULT_MS)
+                    .start());
+            mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+        } else {
+            throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
+        }
+    }
+
+    @Override
+    public void taskVanished(IWindowContainer token) {
+        Objects.requireNonNull(token, "Requires valid IWindowContainer");
+        if (token.asBinder() != mTaskInfo.token.asBinder()) {
+            Log.wtf(TAG, "Unrecognized token: " + token);
+            return;
+        }
+        resizePinnedStackInternal(token, false /* scheduleFinishPip */,
+                mLastReportedBounds, mDisplayBounds,
+                PipAnimationController.DURATION_DEFAULT_MS);
+    }
+
+    @Override
+    public void transactionReady(int id, SurfaceControl.Transaction t) {
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+        resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS);
+    }
+
+    private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect currentBounds, Rect destinationBounds, int animationDurationMs) {
+        try {
+            // Could happen when dismissPip
+            if (wc == null || wc.getLeash() == null) {
+                Log.w(TAG, "Abort animation, invalid leash");
+                return;
+            }
+            final SurfaceControl leash = wc.getLeash();
+            if (animationDurationMs == DURATION_NONE) {
+                // Directly resize if no animation duration is set. When fling, wait for final
+                // callback to issue the proper WindowContainerTransaction with destination bounds.
+                new SurfaceControl.Transaction()
+                        .setPosition(leash, destinationBounds.left, destinationBounds.top)
+                        .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+                        .apply();
+            } else {
+                mMainHandler.post(() -> mPipAnimationController
+                        .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
+                        .setPipAnimationCallback(mPipAnimationCallback)
+                        .setDuration(animationDurationMs)
+                        .start());
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Abort animation, invalid window container", e);
+        } catch (Exception e) {
+            Log.e(TAG, "Should not reach here, terrible thing happened", e);
+        }
+    }
+
+    private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
+        return params == null
+                ? mPipBoundsHandler.getDefaultAspectRatio()
+                : params.getAspectRatio();
+    }
+
+    /**
+     * Callback interface for PiP transitions (both from and to PiP mode)
+     */
+    public interface PipTransitionCallback {
+        /**
+         * Callback when the pip transition is started.
+         */
+        void onPipTransitionStarted();
+
+        /**
+         * Callback when the pip transition is finished.
+         */
+        void onPipTransitionFinished();
+
+        /**
+         * Callback when the pip transition is cancelled.
+         */
+        void onPipTransitionCanceled();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 599c845..4fb675e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -25,6 +25,7 @@
 import android.os.UserManager;
 
 import com.android.systemui.SystemUI;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.io.FileDescriptor;
@@ -98,6 +99,18 @@
         mPipManager.setShelfHeight(visible, height);
     }
 
+    public void setPinnedStackAnimationType(int animationType) {
+        if (mPipManager != null) {
+            mPipManager.setPinnedStackAnimationType(animationType);
+        }
+    }
+
+    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+        if (mPipManager != null) {
+            mPipManager.setPinnedStackAnimationListener(listener);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mPipManager == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index cb94e28..e98dec0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -41,6 +41,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.pip.BasePipManager;
 import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
@@ -59,12 +61,11 @@
  * Manages the picture-in-picture (PIP) UI and states for Phones.
  */
 @Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
     private static final String TAG = "PipManager";
 
     private Context mContext;
     private IActivityManager mActivityManager;
-    private IActivityTaskManager mActivityTaskManager;
     private Handler mHandler = new Handler();
 
     private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
@@ -78,7 +79,9 @@
     private PipMenuActivityController mMenuController;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private PipAppOpsListener mAppOpsListener;
+    private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
 
     /**
      * Handler for display rotation changes.
@@ -124,20 +127,6 @@
         }
 
         @Override
-        public void onPinnedStackAnimationStarted() {
-            // Disable touches while the animation is running
-            mTouchHandler.setTouchEnabled(false);
-        }
-
-        @Override
-        public void onPinnedStackAnimationEnded() {
-            // Re-enable touches after the animation completes
-            mTouchHandler.setTouchEnabled(true);
-            mTouchHandler.onPinnedStackAnimationEnded();
-            mMenuController.onPinnedStackAnimationEnded();
-        }
-
-        @Override
         public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
         }
@@ -149,10 +138,7 @@
     private class PipManagerPinnedStackListener extends PinnedStackListener {
         @Override
         public void onListenerRegistered(IPinnedStackController controller) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.setPinnedStackController(controller);
-                mTouchHandler.setPinnedStackController(controller);
-            });
+            mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
         }
 
         @Override
@@ -164,18 +150,9 @@
         }
 
         @Override
-        public void onMinimizedStateChanged(boolean isMinimized) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
-                mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
-            });
-        }
-
-        @Override
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
             mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment,
-                    fromShelfAdjustment));
+                    false /* fromShelfAdjustment */));
         }
 
         @Override
@@ -207,7 +184,10 @@
 
         @Override
         public void onDisplayInfoChanged(DisplayInfo displayInfo) {
-            mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+            mHandler.post(() -> {
+                mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+                mPipTaskOrganizer.onDisplayInfoChanged(displayInfo);
+            });
         }
 
         @Override
@@ -219,13 +199,6 @@
         public void onAspectRatioChanged(float aspectRatio) {
             mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio));
         }
-
-        @Override
-        public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
-            });
-        }
     }
 
     @Inject
@@ -234,7 +207,6 @@
             FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
-        mActivityTaskManager = ActivityTaskManager.getService();
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -243,24 +215,29 @@
         }
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
 
+        final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
         mPipBoundsHandler = new PipBoundsHandler(context);
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
-        mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
+        mMenuController = new PipMenuActivityController(context, mMediaController,
                 mInputConsumerController);
-        mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
-                mMenuController, mInputConsumerController, mPipBoundsHandler,
+        mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager,
+                mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
                 floatingContentCoordinator);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
         displayController.addDisplayChangingController(mRotationController);
 
-        // If SystemUI restart, and it already existed a pinned stack,
-        // register the pip input consumer to ensure touch can send to it.
         try {
-            ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo(
+            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
+            ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (stackInfo != null) {
+                // If SystemUI restart, and it already existed a pinned stack,
+                // register the pip input consumer to ensure touch can send to it.
                 mInputConsumerController.registerInputConsumer();
             }
         } catch (RemoteException e) {
@@ -320,6 +297,46 @@
         });
     }
 
+    @Override
+    public void setPinnedStackAnimationType(int animationType) {
+        mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+    }
+
+    @Override
+    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+        mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener);
+    }
+
+    @Override
+    public void onPipTransitionStarted() {
+        // Disable touches while the animation is running
+        mTouchHandler.setTouchEnabled(false);
+        if (mPinnedStackAnimationRecentsListener != null) {
+            try {
+                mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to callback recents", e);
+            }
+        }
+    }
+
+    @Override
+    public void onPipTransitionFinished() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    @Override
+    public void onPipTransitionCanceled() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    private void onPipTransitionFinishedOrCanceled() {
+        // Re-enable touches after the animation completes
+        mTouchHandler.setTouchEnabled(true);
+        mTouchHandler.onPinnedStackAnimationEnded();
+        mMenuController.onPinnedStackAnimationEnded();
+    }
+
     private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment,
             boolean fromShelfAdjustment) {
         // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index c7bfc06..d660b67 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
@@ -68,11 +67,8 @@
 
     public static final int MESSAGE_MENU_STATE_CHANGED = 100;
     public static final int MESSAGE_EXPAND_PIP = 101;
-    public static final int MESSAGE_MINIMIZE_PIP = 102;
     public static final int MESSAGE_DISMISS_PIP = 103;
     public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
-    public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;
-    public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106;
     public static final int MESSAGE_SHOW_MENU = 107;
 
     public static final int MENU_STATE_NONE = 0;
@@ -100,11 +96,6 @@
         void onPipExpand();
 
         /**
-         * Called when the PIP requested to be minimized.
-         */
-        void onPipMinimize();
-
-        /**
          * Called when the PIP requested to be dismissed.
          */
         void onPipDismiss();
@@ -116,7 +107,6 @@
     }
 
     private Context mContext;
-    private IActivityManager mActivityManager;
     private PipMediaController mMediaController;
     private InputConsumerController mInputConsumerController;
 
@@ -146,10 +136,6 @@
                     mListeners.forEach(l -> l.onPipExpand());
                     break;
                 }
-                case MESSAGE_MINIMIZE_PIP: {
-                    mListeners.forEach(l -> l.onPipMinimize());
-                    break;
-                }
                 case MESSAGE_DISMISS_PIP: {
                     mListeners.forEach(l -> l.onPipDismiss());
                     break;
@@ -194,10 +180,9 @@
         }
     };
 
-    public PipMenuActivityController(Context context, IActivityManager activityManager,
+    public PipMenuActivityController(Context context,
             PipMediaController mediaController, InputConsumerController inputConsumerController) {
         mContext = context;
-        mActivityManager = activityManager;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index c6e2852..91f539c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.StackInfo;
-import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
@@ -39,7 +38,9 @@
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.os.SomeArgs;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -65,8 +66,6 @@
     /** Friction to use for PIP when it moves via physics fling animations. */
     private static final float DEFAULT_FRICTION = 2f;
 
-    // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
-    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
     // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
     private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
 
@@ -74,10 +73,10 @@
     private static final int MSG_RESIZE_ANIMATE = 2;
     private static final int MSG_OFFSET_ANIMATE = 3;
 
-    private Context mContext;
-    private IActivityManager mActivityManager;
-    private IActivityTaskManager mActivityTaskManager;
-    private Handler mHandler;
+    private final Context mContext;
+    private final IActivityTaskManager mActivityTaskManager;
+    private final PipTaskOrganizer mPipTaskOrganizer;
+    private final Handler mHandler;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -139,14 +138,14 @@
                 new PhysicsAnimator.SpringConfig(
                         SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
 
-    public PipMotionHelper(Context context, IActivityManager activityManager,
-            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
+    public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
+            PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
             PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
             FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mHandler = new Handler(ForegroundThread.get().getLooper(), this);
-        mActivityManager = activityManager;
         mActivityTaskManager = activityTaskManager;
+        mPipTaskOrganizer = pipTaskOrganizer;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFlingAnimationUtils = flingAnimationUtils;
@@ -285,35 +284,6 @@
     }
 
     /**
-     * @return the closest minimized PiP bounds.
-     */
-    Rect getClosestMinimizedBounds(Rect stackBounds) {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds);
-        mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets);
-        return toBounds;
-    }
-
-    /**
-     * @return whether the PiP at the current bounds should be minimized.
-     */
-    boolean shouldMinimizePip() {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        if (mBounds.left < 0) {
-            float offscreenFraction = (float) -mBounds.left / mBounds.width();
-            return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
-        } else if (mBounds.right > displaySize.x) {
-            float offscreenFraction = (float) (mBounds.right - displaySize.x) /
-                    mBounds.width();
-            return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
-        } else {
-            return false;
-        }
-    }
-
-    /**
      * @return whether the PiP at the current bounds should be dismissed.
      */
     boolean shouldDismissPip() {
@@ -328,25 +298,6 @@
     }
 
     /**
-     * Animates the PiP to the minimized state, slightly offscreen.
-     */
-    void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
-        final Rect toBounds = getClosestMinimizedBounds(mBounds);
-
-        mAnimatedBounds.set(mBounds);
-        mAnimatedBoundsPhysicsAnimator
-                .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
-                .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
-
-        if (updateAction != null) {
-            mAnimatedBoundsPhysicsAnimator.addUpdateListener(
-                    (target, values) -> updateAction.run());
-        }
-
-        startBoundsAnimation();
-    }
-
-    /**
      * Flings the PiP to the closest snap target.
      */
     void flingToSnapTarget(
@@ -436,8 +387,7 @@
      * Animates the PiP from the expanded state to the normal state after the menu is hidden.
      */
     void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
-            Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized,
-            boolean immediate) {
+            Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
         if (savedSnapFraction < 0f) {
             // If there are no saved snap fractions, then just use the current bounds
             savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
@@ -445,10 +395,6 @@
         }
         mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
 
-        if (minimized) {
-            normalBounds = getClosestMinimizedBounds(normalBounds);
-        }
-
         if (immediate) {
             movePip(normalBounds);
         } else {
@@ -503,6 +449,7 @@
         cancelAnimations();
 
         mAnimatedBoundsPhysicsAnimator
+                .withEndActions(() ->  mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
                 .addUpdateListener(mResizePipVsyncUpdateListener)
                 .start();
     }
@@ -594,13 +541,6 @@
     }
 
     /**
-     * @return the distance between points {@param p1} and {@param p2}.
-     */
-    private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
-        return PointF.length(r1.left - r2.left, r1.top - r2.top);
-    }
-
-    /**
      * Handles messages to be processed on the background thread.
      */
     public boolean handleMessage(Message msg) {
@@ -608,13 +548,8 @@
             case MSG_RESIZE_IMMEDIATE: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 Rect toBounds = (Rect) args.arg1;
-                try {
-                    mActivityTaskManager.resizePinnedStack(
-                            toBounds, null /* tempPinnedTaskBounds */);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
-                }
+                mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE);
+                mBounds.set(toBounds);
                 return true;
             }
 
@@ -631,8 +566,7 @@
                         return true;
                     }
 
-                    mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds,
-                            duration);
+                    mPipTaskOrganizer.resizePinnedStack(toBounds, duration);
                     mBounds.set(toBounds);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
@@ -654,8 +588,8 @@
                         return true;
                     }
 
-                    mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds,
-                            0/* xOffset */, offset, duration);
+                    mPipTaskOrganizer.offsetPinnedStack(originalBounds,
+                            0 /* xOffset */, offset, duration);
                     Rect toBounds = new Rect(originalBounds);
                     toBounds.offset(0, offset);
                     mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 8e588e6..79a25b2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -45,6 +45,7 @@
 import com.android.systemui.R;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -58,8 +59,6 @@
 public class PipTouchHandler {
     private static final String TAG = "PipTouchHandler";
 
-    // Allow the PIP to be dragged to the edge of the screen to be minimized.
-    private static final boolean ENABLE_MINIMIZE = false;
     // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
     private static final boolean ENABLE_FLING_DISMISS = false;
 
@@ -67,12 +66,9 @@
     private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
 
     // Allow dragging the PIP to a location to close it
-    private final boolean mEnableDimissDragToEdge;
+    private final boolean mEnableDismissDragToEdge;
     private final Context mContext;
     private final IActivityManager mActivityManager;
-    private final IActivityTaskManager mActivityTaskManager;
-    private final ViewConfiguration mViewConfig;
-    private final PipMenuListener mMenuListener = new PipMenuListener();
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
@@ -104,7 +100,7 @@
     private Runnable mShowDismissAffordance = new Runnable() {
         @Override
         public void run() {
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 mDismissViewController.showDismissTarget();
             }
         }
@@ -112,7 +108,6 @@
 
     // Behaviour states
     private int mMenuState = MENU_STATE_NONE;
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
@@ -121,7 +116,6 @@
     private int mMovementBoundsExtraOffsets;
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
-    private boolean mMovementWithinMinimize;
     private boolean mMovementWithinDismiss;
     private PipAccessibilityInteractionConnection mConnection;
 
@@ -146,15 +140,7 @@
 
         @Override
         public void onPipExpand() {
-            if (!mIsMinimized) {
-                mMotionHelper.expandPip();
-            }
-        }
-
-        @Override
-        public void onPipMinimize() {
-            setMinimizedStateInternal(true);
-            mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
+            mMotionHelper.expandPip();
         }
 
         @Override
@@ -175,26 +161,24 @@
             IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
             InputConsumerController inputConsumerController,
             PipBoundsHandler pipBoundsHandler,
+            PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator) {
-
         // Initialize the Pip input consumer
         mContext = context;
         mActivityManager = activityManager;
-        mActivityTaskManager = activityTaskManager;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
-        mViewConfig = ViewConfiguration.get(context);
         mMenuController = menuController;
-        mMenuController.addListener(mMenuListener);
+        mMenuController.addListener(new PipMenuListener());
         mDismissViewController = new PipDismissViewController(context);
         mSnapAlgorithm = new PipSnapAlgorithm(mContext);
         mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
                 2.5f);
         mGesture = new DefaultPipTouchGesture();
-        mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
+        mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
                 mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
-        mTouchState = new PipTouchState(mViewConfig, mHandler,
+        mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
 
@@ -203,7 +187,7 @@
                 R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
-        mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
 
         // Register the listener for input consumer touch events
         inputConsumerController.setInputListener(this::handleTouchEvent);
@@ -339,8 +323,7 @@
         // If we have a deferred resize, apply it now
         if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
             mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                    mNormalMovementBounds, mMovementBounds, mIsMinimized,
-                    true /* immediate */);
+                    mNormalMovementBounds, mMovementBounds, true /* immediate */);
             mSavedSnapFraction = -1f;
             mDeferResizeToNormalBoundsUntilRotation = -1;
         }
@@ -482,44 +465,6 @@
     }
 
     /**
-     * Sets the minimized state.
-     */
-    private void setMinimizedStateInternal(boolean isMinimized) {
-        if (!ENABLE_MINIMIZE) {
-            return;
-        }
-        setMinimizedState(isMinimized, false /* fromController */);
-    }
-
-    /**
-     * Sets the minimized state.
-     */
-    void setMinimizedState(boolean isMinimized, boolean fromController) {
-        if (!ENABLE_MINIMIZE) {
-            return;
-        }
-        if (mIsMinimized != isMinimized) {
-            MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
-                    isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
-        }
-        mIsMinimized = isMinimized;
-        mSnapAlgorithm.setMinimized(isMinimized);
-
-        if (fromController) {
-            if (isMinimized) {
-                // Move the PiP to the new bounds immediately if minimized
-                mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds));
-            }
-        } else if (mPinnedStackController != null) {
-            try {
-                mPinnedStackController.setIsMinimized(isMinimized);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not set minimized state", e);
-            }
-        }
-    }
-
-    /**
      * Sets the menu visibility.
      */
     private void setMenuState(int menuState, boolean resize) {
@@ -562,8 +507,7 @@
                 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
                     Rect normalBounds = new Rect(mNormalBounds);
                     mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                            mNormalMovementBounds, mMovementBounds, mIsMinimized,
-                            false /* immediate */);
+                            mNormalMovementBounds, mMovementBounds, false /* immediate */);
                     mSavedSnapFraction = -1f;
                 }
             } else {
@@ -601,8 +545,6 @@
      * Gesture controlling normal movement of the PIP.
      */
     private class DefaultPipTouchGesture extends PipTouchGesture {
-        // Whether the PiP was on the left side of the screen at the start of the gesture
-        private boolean mStartedOnLeft;
         private final Point mStartPosition = new Point();
         private final PointF mDelta = new PointF();
 
@@ -615,17 +557,15 @@
             Rect bounds = mMotionHelper.getBounds();
             mDelta.set(0f, 0f);
             mStartPosition.set(bounds.left, bounds.top);
-            mStartedOnLeft = bounds.left < mMovementBounds.centerX();
-            mMovementWithinMinimize = true;
             mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
 
-            // If the menu is still visible, and we aren't minimized, then just poke the menu
+            // If the menu is still visible then just poke the menu
             // so that it will timeout after the user stops touching it
-            if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
+            if (mMenuState != MENU_STATE_NONE) {
                 mMenuController.pokeMenu();
             }
 
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
             }
@@ -640,7 +580,7 @@
             if (touchState.startedDragging()) {
                 mSavedSnapFraction = -1f;
 
-                if (mEnableDimissDragToEdge) {
+                if (mEnableDismissDragToEdge) {
                     mHandler.removeCallbacks(mShowDismissAffordance);
                     mDismissViewController.showDismissTarget();
                 }
@@ -662,17 +602,11 @@
                 mTmpBounds.offsetTo((int) left, (int) top);
                 mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
 
-                if (mEnableDimissDragToEdge) {
+                if (mEnableDismissDragToEdge) {
                     updateDismissFraction();
                 }
 
                 final PointF curPos = touchState.getLastTouchPosition();
-                if (mMovementWithinMinimize) {
-                    // Track if movement remains near starting edge to identify swipes to minimize
-                    mMovementWithinMinimize = mStartedOnLeft
-                            ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
-                            : curPos.x >= mMovementBounds.right;
-                }
                 if (mMovementWithinDismiss) {
                     // Track if movement remains near the bottom edge to identify swipe to dismiss
                     mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
@@ -684,7 +618,7 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 // Clean up the dismiss target regardless of the touch state in case the touch
                 // enabled state changes while the user is interacting
                 cleanUpDismissTarget();
@@ -704,7 +638,7 @@
                             vel.y, isFling);
             final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
                     && (mMovementWithinDismiss || isUpWithinDimiss);
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 // Check if the user dragged or flung the PiP offscreen to dismiss it
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
@@ -717,33 +651,10 @@
             }
 
             if (touchState.isDragging()) {
-                final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
-                        && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
-                if (ENABLE_MINIMIZE &&
-                        !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
-                    // Pip should be minimized
-                    setMinimizedStateInternal(true);
-                    if (mMenuState == MENU_STATE_FULL) {
-                        // If the user dragged the expanded PiP to the edge, then hiding the menu
-                        // will trigger the PiP to be scaled back to the normal size with the
-                        // minimize offset adjusted
-                        mMenuController.hideMenu();
-                    } else {
-                        mMotionHelper.animateToClosestMinimizedState(
-                                PipTouchHandler.this::updateDismissFraction /* updateAction */);
-                    }
-                    return true;
-                }
-                if (mIsMinimized) {
-                    // If we're dragging and it wasn't a minimize gesture then we shouldn't be
-                    // minimized.
-                    setMinimizedStateInternal(false);
-                }
-
                 Runnable endAction = null;
                 if (mMenuState != MENU_STATE_NONE) {
-                    // If the menu is still visible, and we aren't minimized, then just poke the
-                    // menu so that it will timeout after the user stops touching it
+                    // If the menu is still visible, then just poke the menu so that
+                    // it will timeout after the user stops touching it
                     mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
                             mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
                 } else {
@@ -759,10 +670,6 @@
                 } else {
                     mMotionHelper.animateToClosestSnapTarget();
                 }
-            } else if (mIsMinimized) {
-                // This was a tap, so no longer minimized
-                mMotionHelper.animateToClosestSnapTarget();
-                setMinimizedStateInternal(false);
             } else if (mTouchState.isDoubleTap()) {
                 // Expand to fullscreen if this is a double tap
                 mMotionHelper.expandPip();
@@ -821,14 +728,12 @@
         pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
         pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
         pw.println(innerPrefix + "mMenuState=" + mMenuState);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
         pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
-        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge);
-        pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
+        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
         mSnapAlgorithm.dump(pw, innerPrefix);
         mTouchState.dump(pw, innerPrefix);
         mMotionHelper.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 487c253..cb1a218 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -50,7 +50,9 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -66,7 +68,7 @@
  * Manages the picture-in-picture (PIP) UI and states.
  */
 @Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
     private static final String TAG = "PipManager";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -91,7 +93,6 @@
     private static final int INVALID_RESOURCE_TYPE = -1;
 
     public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
-    public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
 
     /**
      * PIPed activity is playing a media and it can be paused.
@@ -112,6 +113,7 @@
 
     private Context mContext;
     private PipBoundsHandler mPipBoundsHandler;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private IActivityTaskManager mActivityTaskManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
@@ -205,8 +207,7 @@
         }
 
         @Override
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
             mHandler.post(() -> {
                 // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
                 mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
@@ -234,6 +235,8 @@
         mInitialized = true;
         mContext = context;
         mPipBoundsHandler = new PipBoundsHandler(context);
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
@@ -279,9 +282,12 @@
 
         mMediaSessionManager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
+            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -422,20 +428,13 @@
             case STATE_PIP_MENU:
                 mCurrentPipBounds = mMenuModePipBounds;
                 break;
-            case STATE_PIP:
-                mCurrentPipBounds = mPipBounds;
-                break;
+            case STATE_PIP: // fallthrough
             default:
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
-        try {
-            int animationDurationMs = -1;
-            mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds,
-                    animationDurationMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "resizeStack failed", e);
-        }
+        mPipTaskOrganizer.resizePinnedStack(
+                mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS);
     }
 
     /**
@@ -449,13 +448,6 @@
     }
 
     /**
-     * Returns the default PIP bound.
-     */
-    public Rect getPipBounds() {
-        return mPipBounds;
-    }
-
-    /**
      * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
      * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
      */
@@ -692,19 +684,28 @@
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
-
-        @Override
-        public void onPinnedStackAnimationEnded() {
-            if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-
-            switch (getState()) {
-                case STATE_PIP_MENU:
-                    showPipMenu();
-                    break;
-            }
-        }
     };
 
+    @Override
+    public void onPipTransitionStarted() { }
+
+    @Override
+    public void onPipTransitionFinished() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    @Override
+    public void onPipTransitionCanceled() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    private void onPipTransitionFinishedOrCanceled() {
+        if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+        if (getState() == STATE_PIP_MENU) {
+            showPipMenu();
+        }
+    }
+
     /**
      * A listener interface to receive notification on changes in PIP.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 34cad51..1cd6388 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,9 +61,11 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.Dumpable;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipUI;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -388,6 +390,32 @@
             }
         }
 
+        @Override
+        public void notifySwipeToHomeFinished() {
+            if (!verifyCaller("notifySwipeToHomeFinished")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+            if (!verifyCaller("setPinnedStackAnimationListener")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mPipUI.setPinnedStackAnimationListener(listener);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
new file mode 100644
index 0000000..6c09a46
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.systemui.pip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks
+ * depending on the various interactions.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipAnimationControllerTest extends SysuiTestCase {
+
+    private PipAnimationController mPipAnimationController;
+
+    @Mock
+    private IWindowContainer mWindowContainer;
+
+    @Mock
+    private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mPipAnimationController = new PipAnimationController(mContext);
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void getAnimator_withAlpha_returnFloatAnimator() {
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+
+        assertEquals("Expect ANIM_TYPE_ALPHA animation",
+                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
+    }
+
+    @Test
+    public void getAnimator_withBounds_returnBoundsAnimator() {
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), new Rect());
+
+        assertEquals("Expect ANIM_TYPE_BOUNDS animation",
+                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
+    }
+
+    @Test
+    public void getAnimator_whenSameTypeRunning_updateExistingAnimator() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue1 = new Rect(100, 100, 200, 200);
+        final Rect endValue2 = new Rect(200, 200, 300, 300);
+        final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue1);
+        oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+        oldAnimator.start();
+
+        final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue2);
+
+        assertEquals("getAnimator with same type returns same animator",
+                oldAnimator, newAnimator);
+        assertEquals("getAnimator with same type updates end value",
+                endValue2, newAnimator.getEndValue());
+    }
+
+    @Test
+    public void getAnimator_scheduleFinishPip() {
+        PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+        assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip());
+
+        animator = mPipAnimationController
+                .getAnimator(mWindowContainer, false /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+        assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip());
+    }
+
+    @Test
+    public void pipTransitionAnimator_updateEndValue() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue1 = new Rect(100, 100, 200, 200);
+        final Rect endValue2 = new Rect(200, 200, 300, 300);
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue1);
+
+        animator.updateEndValue(endValue2);
+
+        assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2);
+    }
+
+    @Test
+    public void pipTransitionAnimator_setPipAnimationCallback() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue = new Rect(100, 100, 200, 200);
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue);
+        animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+
+        animator.setPipAnimationCallback(mPipAnimationCallback);
+
+        // onAnimationStart triggers onPipAnimationStart
+        animator.onAnimationStart(animator);
+        verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator);
+
+        // onAnimationCancel triggers onPipAnimationCancel
+        animator.onAnimationCancel(animator);
+        verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator);
+
+        // onAnimationEnd triggers onPipAnimationEnd
+        animator.onAnimationEnd(animator);
+        verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer),
+                any(SurfaceControl.Transaction.class), eq(animator));
+    }
+
+    /**
+     * A dummy {@link SurfaceControl.Transaction} class.
+     * This is created as {@link Mock} does not support method chaining.
+     */
+    private static class DummySurfaceControlTx extends SurfaceControl.Transaction {
+        @Override
+        public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
+            return this;
+        }
+
+        @Override
+        public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
+            return this;
+        }
+
+        @Override
+        public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
+            return this;
+        }
+
+        @Override
+        public void apply() {}
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index bc3ce8b..1dbcf10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -16,13 +16,7 @@
 
 package com.android.systemui.pip;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
@@ -31,7 +25,6 @@
 import android.testing.TestableResources;
 import android.view.DisplayInfo;
 import android.view.Gravity;
-import android.view.IPinnedStackController;
 
 import androidx.test.filters.SmallTest;
 
@@ -40,9 +33,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Unit tests against {@link PipBoundsHandler}, including but not limited to:
@@ -55,22 +45,18 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class PipBoundsHandlerTest extends SysuiTestCase {
     private static final int ROUNDING_ERROR_MARGIN = 10;
+    private static final float DEFAULT_ASPECT_RATIO = 1f;
+    private static final Rect EMPTY_CURRENT_BOUNDS = null;
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
-    private Rect mDefaultDisplayRect;
-
-    @Mock
-    private IPinnedStackController mPinnedStackController;
 
     @Before
     public void setUp() throws Exception {
         mPipBoundsHandler = new PipBoundsHandler(mContext);
-        MockitoAnnotations.initMocks(this);
         initializeMockResources();
 
         mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
-        mPipBoundsHandler.setPinnedStackController(mPinnedStackController);
     }
 
     private void initializeMockResources() {
@@ -94,142 +80,80 @@
         mDefaultDisplayInfo.displayId = 1;
         mDefaultDisplayInfo.logicalWidth = 1000;
         mDefaultDisplayInfo.logicalHeight = 1500;
-        mDefaultDisplayRect = new Rect(0, 0,
-                mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight);
     }
 
     @Test
-    public void setShelfHeight_offsetBounds() throws Exception {
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+    public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        lastPosition.offset(0, -shelfHeight);
-        assertBoundsWithMargin("PiP bounds offset by shelf height",
-                lastPosition, destinationBounds.getValue());
+        oldPosition.offset(0, -shelfHeight);
+        assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition);
     }
 
     @Test
-    public void onImeVisibilityChanged_offsetBounds() throws Exception {
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+    public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        lastPosition.offset(0, -imeHeight);
-        assertBoundsWithMargin("PiP bounds offset by IME height",
-                lastPosition, destinationBounds.getValue());
+        oldPosition.offset(0, -imeHeight);
+        assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition);
     }
 
     @Test
-    public void onPrepareAnimation_startAnimation() throws Exception {
-        final Rect sourceRectHint = new Rect(100, 100, 200, 200);
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), eq(sourceRectHint), anyInt());
-        final Rect capturedDestinationBounds = destinationBounds.getValue();
-        assertFalse("Destination bounds is not empty",
-                capturedDestinationBounds.isEmpty());
-        assertBoundsWithMargin("Destination bounds within Display",
-                mDefaultDisplayRect, capturedDestinationBounds);
-    }
-
-    @Test
-    public void onSaveReentryBounds_restoreLastPosition() throws Exception {
+    public void onSaveReentryBounds_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        oldPosition.offset(0, -100);
+        mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        lastPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        assertBoundsWithMargin("Last position is restored",
-                lastPosition, destinationBounds.getValue());
+        assertBoundsWithMargin("Last position is restored", oldPosition, newPosition);
     }
 
     @Test
-    public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception {
+    public void onResetReentryBounds_componentMatch_useDefaultBounds() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect defaultBounds = new Rect(destinationBounds.getValue());
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
 
         mPipBoundsHandler.onResetReentryBounds(componentName);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect actualBounds = destinationBounds.getValue();
         assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds);
     }
 
     @Test
-    public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception {
+    public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect defaultBounds = new Rect(destinationBounds.getValue());
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
 
         mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect actualBounds = destinationBounds.getValue();
         assertBoundsWithMargin("Last position is restored", newBounds, actualBounds);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c7f5f63..bf79729 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2570,8 +2570,6 @@
         switch (op) {
             case "move-task":
                 return runStackMoveTask(pw);
-            case "resize-animated":
-                return runStackResizeAnimated(pw);
             case "resize-docked-stack":
                 return runStackResizeDocked(pw);
             case "positiontask":
@@ -2648,23 +2646,6 @@
         return 0;
     }
 
-    int runStackResizeAnimated(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
-        final Rect bounds;
-        if ("null".equals(peekNextArg())) {
-            bounds = null;
-        } else {
-            bounds = getBounds();
-            if (bounds == null) {
-                getErrPrintWriter().println("Error: invalid input bounds");
-                return -1;
-            }
-        }
-        mTaskInterface.animateResizePinnedStack(stackId, bounds, -1);
-        return 0;
-    }
-
     int runStackResizeDocked(PrintWriter pw) throws RemoteException {
         final Rect bounds = getBounds();
         final Rect taskBounds = getBounds();
@@ -3285,8 +3266,6 @@
             pw.println("       move-task <TASK_ID> <STACK_ID> [true|false]");
             pw.println("           Move <TASK_ID> from its current stack to the top (true) or");
             pw.println("           bottom (false) of <STACK_ID>.");
-            pw.println("       resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
-            pw.println("           Same as resize, but allow animation.");
             pw.println("       resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]");
             pw.println("           Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
             pw.println("           and supplying temporary different task bounds indicated by");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 190af7a..d715ed4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3845,13 +3845,6 @@
                     mDisplayContent.setLayoutNeeded();
                 }
                 mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
-
-                // Notify the pinned stack upon all windows drawn. If there was an animation in
-                // progress then this signal will resume that animation.
-                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
-                if (pinnedStack != null) {
-                    pinnedStack.onAllWindowsDrawn();
-                }
             }
         }
     }
@@ -6755,18 +6748,8 @@
         // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
         final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
         if (pinnedStack == null) return;
-        final Rect stackBounds;
-        if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
-            // We are animating the bounds, use the pre-animation bounds to save the snap
-            // fraction
-            stackBounds = pinnedStack.mPreAnimationBounds;
-        } else {
-            // We skip the animation if the fullscreen configuration is not compatible, so
-            // use the current bounds to calculate the saved snap fraction instead
-            // (see PinnedActivityStack.skipResizeAnimation())
-            stackBounds = mTmpRect;
-            pinnedStack.getBounds(stackBounds);
-        }
+        final Rect stackBounds = mTmpRect;
+        pinnedStack.getBounds(stackBounds);
         mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds(
                 mActivityComponent, stackBounds);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2f1cc05..ff890ff 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -98,10 +98,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.TaskProto.ACTIVITIES;
 import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
@@ -202,7 +198,7 @@
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack extends Task implements BoundsAnimationTarget {
+class ActivityStack extends Task {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -327,11 +323,8 @@
     // Set when an animation has been requested but has not yet started from the UI thread. This is
     // cleared when the animation actually starts.
     private boolean mBoundsAnimatingRequested = false;
-    private boolean mBoundsAnimatingToFullscreen = false;
-    private boolean mCancelCurrentBoundsAnimation = false;
     private Rect mBoundsAnimationTarget = new Rect();
     private Rect mBoundsAnimationSourceHintBounds = new Rect();
-    private @BoundsAnimationController.AnimationType int mAnimationType;
 
     Rect mPreAnimationBounds = new Rect();
 
@@ -790,17 +783,6 @@
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 false /* creating */);
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            // This stack will be visible before SystemUI requests PiP animation to start, and if
-            // the selected animation type is fade in, this stack will be close first. If the
-            // animation does not start early, user may see this full screen window appear again
-            // after the closing transition finish, which could cause screen to flicker.
-            // Check if animation should start here in advance.
-            final BoundsAnimationController controller = getDisplay().mBoundsAnimationController;
-            if (controller.isAnimationTypeFadeIn()) {
-                setPinnedStackAlpha(0f);
-            }
-        }
     }
 
     /**
@@ -905,6 +887,9 @@
             likelyResolvedMode = parent != null ? parent.getWindowingMode()
                     : WINDOWING_MODE_FULLSCREEN;
         }
+        if (currentMode == WINDOWING_MODE_PINNED) {
+            mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+        }
         if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
                 && topActivity != null && !topActivity.noDisplay
                 && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
@@ -3480,91 +3465,6 @@
         }
     }
 
-    void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration,
-            boolean fromFullscreen) {
-        if (!inPinnedWindowingMode()) return;
-
-        /**
-         * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
-         * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely
-         * on the TaskOrganizer side, so we just hand over the leash without doing any animation.
-         * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer
-         * needs to have flexibility to schedule that at an appropriate point in the animation.
-         */
-        if (isControlledByTaskOrganizer()) return;
-        if (toBounds == null /* toFullscreen */) {
-            final Configuration parentConfig = getParent().getConfiguration();
-            final ActivityRecord top = topRunningNonOverlayTaskActivity();
-            if (top != null && !top.isConfigurationCompatible(parentConfig)) {
-                // The final orientation of this activity will change after moving to full screen.
-                // Start freezing screen here to prevent showing a temporary full screen window.
-                top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
-                dismissPip();
-                return;
-            }
-        }
-
-        // Get the from-bounds
-        final Rect fromBounds = new Rect();
-        getBounds(fromBounds);
-
-        // Get non-null fullscreen to-bounds for animating if the bounds are null
-        @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-        final boolean toFullscreen = toBounds == null;
-        if (toFullscreen) {
-            if (fromFullscreen) {
-                throw new IllegalArgumentException("Should not defer scheduling PiP mode"
-                        + " change on animation to fullscreen.");
-            }
-            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
-            mWmService.getStackBounds(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
-            if (!mTmpToBounds.isEmpty()) {
-                // If there is a fullscreen bounds, use that
-                toBounds = new Rect(mTmpToBounds);
-            } else {
-                // Otherwise, use the display bounds
-                toBounds = new Rect();
-                getDisplayContent().getBounds(toBounds);
-            }
-        } else if (fromFullscreen) {
-            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-        }
-
-        setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
-        final Rect finalToBounds = toBounds;
-        final @BoundsAnimationController.SchedulePipModeChangedState int
-                finalSchedulePipModeChangedState = schedulePipModeChangedState;
-        final DisplayContent displayContent = getDisplayContent();
-        @BoundsAnimationController.AnimationType int intendedAnimationType =
-                displayContent.mBoundsAnimationController.getAnimationType();
-        if (intendedAnimationType == FADE_IN) {
-            if (fromFullscreen) {
-                setPinnedStackAlpha(0f);
-            }
-            if (toBounds.width() == fromBounds.width()
-                    && toBounds.height() == fromBounds.height()) {
-                intendedAnimationType = BoundsAnimationController.BOUNDS;
-            } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
-                // intendedAnimationType may have been reset at the end of RecentsAnimation,
-                // force it to BOUNDS type if we know for certain we're animating to
-                // a different bounds, especially for expand and collapse of PiP window.
-                intendedAnimationType = BoundsAnimationController.BOUNDS;
-            }
-        }
-
-        final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
-        mCancelCurrentBoundsAnimation = false;
-        displayContent.mBoundsAnimationController.getHandler().post(() -> {
-            displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
-                    finalToBounds, animationDuration, finalSchedulePipModeChangedState,
-                    fromFullscreen, toFullscreen, animationType);
-        });
-    }
-
     void dismissPip() {
         if (!isActivityTypeStandardOrUndefined()) {
             throw new IllegalArgumentException(
@@ -3593,21 +3493,6 @@
         });
     }
 
-    private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        // It is guaranteed that the activities requiring the update will be in the pinned stack at
-        // this point (either reparented before the animation into PiP, or before reparenting after
-        // the animation out of PiP)
-        if (!isAttached()) {
-            return;
-        }
-        final PooledConsumer c = PooledLambda.obtainConsumer(
-                ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
-                PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
-        forAllLeafTasks(c, true /* traverseTopToBottom */);
-        c.recycle();
-    }
-
     void prepareFreezingTaskBounds() {
         forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
     }
@@ -3704,34 +3589,6 @@
     }
 
     /**
-     * Sets the bounds animation target bounds ahead of an animation.  This can't currently be done
-     * in onAnimationStart() since that is started on the UiThread.
-     */
-    private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
-            boolean toFullscreen) {
-        if (mAnimationType == BoundsAnimationController.BOUNDS) {
-            mBoundsAnimatingRequested = true;
-        }
-        mBoundsAnimatingToFullscreen = toFullscreen;
-        if (destBounds != null) {
-            mBoundsAnimationTarget.set(destBounds);
-        } else {
-            mBoundsAnimationTarget.setEmpty();
-        }
-        if (sourceHintBounds != null) {
-            mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
-        } else if (!mBoundsAnimating) {
-            // If the bounds are already animating, we don't want to reset the source hint. This is
-            // because the source hint is sent when starting the animation from the client that
-            // requested to enter pip. Other requests can adjust the pip bounds during an animation,
-            // but could accidentally reset the source hint bounds.
-            mBoundsAnimationSourceHintBounds.setEmpty();
-        }
-
-        mPreAnimationBounds.set(getRawBounds());
-    }
-
-    /**
      * @return the final bounds for the bounds animation.
      */
     void getFinalAnimationBounds(Rect outBounds) {
@@ -3763,30 +3620,6 @@
     }
 
     /**
-     * Reset the current animation running on {@link #mBoundsAnimationTarget}.
-     *
-     * @param destinationBounds the final destination bounds
-     */
-    void resetCurrentBoundsAnimation(Rect destinationBounds) {
-        boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating)
-                && !mBoundsAnimationTarget.isEmpty();
-
-        // The final boundary is updated while there is an existing boundary animation. Let's
-        // cancel this animation to prevent the obsolete animation overwritten updated bounds.
-        if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) {
-            final BoundsAnimationController controller =
-                    getDisplayContent().mBoundsAnimationController;
-            controller.getHandler().post(() -> controller.cancel(this));
-        }
-        // Once we've set the bounds based on the rotation of the old bounds in the new
-        // orientation, clear the animation target bounds since they are obsolete, and
-        // cancel any currently running animations
-        mBoundsAnimationTarget.setEmpty();
-        mBoundsAnimationSourceHintBounds.setEmpty();
-        mCancelCurrentBoundsAnimation = true;
-    }
-
-    /**
      * Updates the passed-in {@code inOutBounds} based on the current state of the
      * docked controller. This gets run *after* the override configuration is updated, so it's
      * safe to rely on the controller's state in here (though eventually this dependence should
@@ -4590,110 +4423,6 @@
         return task != null;
     }
 
-    public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            if (mCancelCurrentBoundsAnimation) {
-                return false;
-            }
-            mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
-        }
-
-        return true;
-    }
-
-    void onAllWindowsDrawn() {
-        if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
-            return;
-        }
-
-        getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
-    }
-
-    @Override  // AnimatesBounds
-    public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
-            @BoundsAnimationController.AnimationType int animationType) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            if (!isAttached()) {
-                // Don't run the animation if the stack is already detached
-                return false;
-            }
-
-            if (animationType == BoundsAnimationController.BOUNDS) {
-                mBoundsAnimatingRequested = false;
-                mBoundsAnimating = true;
-            }
-            mAnimationType = animationType;
-
-            // 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 (inPinnedWindowingMode()) {
-            try {
-                mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
-            } catch (RemoteException e) {
-                // I don't believe you...
-            }
-
-            if ((schedulePipModeChangedCallback || animationType == FADE_IN)) {
-                // We need to schedule the PiP mode change before the animation up. It is possible
-                // in this case for the animation down to not have been completed, so always
-                // force-schedule and update to the client to ensure that it is notified that it
-                // is no longer in picture-in-picture mode
-                updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
-            }
-        }
-        return true;
-    }
-
-    @Override  // AnimatesBounds
-    public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
-            boolean moveToFullscreen) {
-        synchronized (mWmService.mGlobalLock) {
-            if (inPinnedWindowingMode()) {
-                // Update to the final bounds if requested. This is done here instead of in the
-                // bounds animator to allow us to coordinate this after we notify the PiP mode
-                // changed
-
-                if (schedulePipModeChangedCallback) {
-                    // We need to schedule the PiP mode change after the animation down, so use the
-                    // final bounds
-                    updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget,
-                            false /* forceUpdate */);
-                }
-
-                if (mAnimationType == BoundsAnimationController.FADE_IN) {
-                    setPinnedStackAlpha(1f);
-                    mWmService.mAtmService.notifyPinnedStackAnimationEnded();
-                    return;
-                }
-
-                if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
-                    setPinnedStackSize(finalStackSize, null);
-                } else {
-                    // We have been canceled, so the final stack size is null, still run the
-                    // animation-end logic
-                    onPipAnimationEndResize();
-                }
-
-                mWmService.mAtmService.notifyPinnedStackAnimationEnded();
-                if (moveToFullscreen) {
-                    ((ActivityStack) this).dismissPip();
-                }
-            } else {
-                // No PiP animation, just run the normal animation-end logic
-                onPipAnimationEndResize();
-            }
-        }
-    }
-
     /**
      * Sets the current picture-in-picture aspect ratio.
      */
@@ -4741,42 +4470,6 @@
         getDisplayContent().getPinnedStackController().setActions(actions);
     }
 
-    /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
-    void onPipAnimationEndResize() {
-        mBoundsAnimating = false;
-        forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */);
-        mWmService.requestTraversal();
-    }
-
-    @Override
-    public boolean shouldDeferStartOnMoveToFullscreen() {
-        synchronized (mWmService.mGlobalLock) {
-            if (!isAttached()) {
-                // Unnecessary to pause the animation because the stack is detached.
-                return false;
-            }
-
-            // Workaround for the recents animation -- normally we need to wait for the new activity
-            // to show before starting the PiP animation, but because we start and show the home
-            // activity early for the recents animation prior to the PiP animation starting, there
-            // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
-            // stack is already visible and drawn.
-            final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
-            if (homeStack == null) {
-                return true;
-            }
-            final Task homeTask = homeStack.getTopMostTask();
-            if (homeTask == null) {
-                return true;
-            }
-            final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
-            if (!homeTask.isVisible() || homeApp == null) {
-                return true;
-            }
-            return !homeApp.allDrawn;
-        }
-    }
-
     /**
      * @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.
@@ -4798,25 +4491,6 @@
         return mBoundsAnimating;
     }
 
-    public boolean isAnimatingBounds() {
-        return mBoundsAnimating;
-    }
-
-    public boolean lastAnimatingBoundsWasToFullscreen() {
-        return mBoundsAnimatingToFullscreen;
-    }
-
-    public boolean isAnimatingBoundsToFullscreen() {
-        return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
-    }
-
-    public boolean pinnedStackResizeDisallowed() {
-        if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
-            return true;
-        }
-        return false;
-    }
-
     /** Returns true if a removal action is still being deferred. */
     boolean checkCompleteDeferredRemoval() {
         if (isAnimating(TRANSITION | CHILDREN)) {
@@ -4843,21 +4517,6 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
-    @Override
-    public boolean setPinnedStackAlpha(float alpha) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            final SurfaceControl sc = getSurfaceControl();
-            if (sc == null || !sc.isValid()) {
-                // If the stack is already removed, don't bother updating any stack animation
-                return false;
-            }
-            getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
-            scheduleAnimation();
-            return !mCancelCurrentBoundsAnimation;
-        }
-    }
-
     public DisplayInfo getDisplayInfo() {
         return mDisplayContent.getDisplayInfo();
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 97b6388..b2d2f62 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1696,40 +1696,6 @@
         }
     }
 
-    void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) {
-        // TODO(multi-display): The display containing the stack should be passed in.
-        final ActivityStack stack =
-                mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
-        if (stack == null) {
-            Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
-        mService.deferWindowLayout();
-        try {
-            Rect configBounds = null;
-            if (inConfigBounds != null) {
-                // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
-                configBounds = tempRect;
-                configBounds.top = 0;
-                configBounds.left = 0;
-                configBounds.right = inConfigBounds.width();
-                configBounds.bottom = inConfigBounds.height();
-            }
-            if (displayedBounds != null && inConfigBounds == null) {
-                // We have finished the animation into PiP, and are resizing the tasks to match the
-                // stack bounds, while layouts are deferred, update any task state as a part of
-                // transitioning it from fullscreen into a floating state.
-                stack.onPipAnimationEndResize();
-            }
-            stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
-        } finally {
-            mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
     private void removeStackInSurfaceTransaction(ActivityStack stack) {
         if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
             /**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 600a125..a467e73 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -97,7 +97,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -2401,7 +2400,6 @@
                 mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
         addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
-        updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
 
         if (DEBUG_TASKS) {
             Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
@@ -2424,21 +2422,6 @@
         mIntentDelivered = true;
     }
 
-    @VisibleForTesting
-    void updateBounds(Task task, Rect bounds) {
-        if (bounds.isEmpty()) {
-            return;
-        }
-
-        final Task rootTask = task.getRootTask();
-        if (rootTask != null && rootTask.inPinnedWindowingMode()) {
-            mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1);
-        } else {
-            // TODO: I don't believe it is possible to reach this else condition anymore...
-            task.setBounds(bounds);
-        }
-    }
-
     private void addOrReparentStartingActivity(Task parent, String reason) {
         if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
             parent.addChild(mStartActivity);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5392257..9458939 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2645,64 +2645,6 @@
         }
     }
 
-    @Override
-    public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stackId
-                        + " doesn't support animated resize.");
-                }
-                stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
-                        animationDuration, false /* fromFullscreen */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset,
-            int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                if (xOffset == 0 && yOffset == 0) {
-                    return;
-                }
-                final ActivityStack stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stackId
-                            + " doesn't support animated resize.");
-                }
-                final Rect destBounds = new Rect();
-                stack.getAnimationOrCurrentBounds(destBounds);
-                if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
-                    Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete.");
-                    return;
-                }
-                destBounds.offset(xOffset, yOffset);
-                stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
-                        animationDuration, false /* fromFullscreen */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
     /**
      * Moves the specified task to the primary-split-screen stack.
      *
@@ -4016,17 +3958,7 @@
                     throw new IllegalArgumentException("Stack: " + stack
                             + " doesn't support animated resize.");
                 }
-                /**
-                 * TODO(b/146594635): Remove all PIP animation code from WM
-                 * once SysUI handles animation. Don't even try to animate TaskOrganized tasks.
-                 */
-                if (animate && !stack.isControlledByTaskOrganizer()) {
-                    stack.animateResizePinnedStack(null /* destBounds */,
-                            null /* sourceHintBounds */, animationDuration,
-                            false /* fromFullscreen */);
-                } else {
-                    stack.dismissPip();
-                }
+                stack.dismissPip();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -4121,15 +4053,10 @@
     }
 
     private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode()
-                || r.getRootTask().isInStackLocked(r) == null) {
-            return false;
-        }
-
-        // If we are animating to fullscreen then we have already dispatched the PIP mode
-        // changed, so we should reflect that check here as well.
-        final ActivityStack taskStack = r.getRootTask();
-        return !taskStack.isAnimatingBoundsToFullscreen();
+        return r != null
+                && r.getRootTask() != null
+                && r.inPinnedWindowingMode()
+                && r.getRootTask().isInStackLocked(r) != null;
     }
 
     @Override
@@ -4213,11 +4140,9 @@
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
                     final ActivityStack stack = r.getRootTask();
-                    if (!stack.isAnimatingBoundsToFullscreen()) {
-                        stack.setPictureInPictureAspectRatio(
-                                r.pictureInPictureArgs.getAspectRatio());
-                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
-                    }
+                    stack.setPictureInPictureAspectRatio(
+                            r.pictureInPictureArgs.getAspectRatio());
+                    stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
                 logPictureInPictureArgs(params);
             }
@@ -4421,31 +4346,6 @@
                 .supportsLocalVoiceInteraction();
     }
 
-    /** Notifies all listeners when the pinned stack animation starts. */
-    @Override
-    public void notifyPinnedStackAnimationStarted() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    @Override
-    public void notifyPinnedStackAnimationEnded() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
-    }
-
-    @Override
-    public void resizePinnedStack(Rect displayedBounds, Rect configBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
deleted file mode 100644
index 5385e2f..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * Copyright (C) 2016 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.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.animation.AnimationHandler;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Enables animating bounds of objects.
- *
- * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
- * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
- * relaunching it would cause poorer experience), these class provides a way to directly animate
- * the bounds of the resized object.
- *
- * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
- *
- * NOTE: All calls to methods in this class should be done on the Animation thread
- */
-public class BoundsAnimationController {
-    private static final boolean DEBUG_LOCAL = false;
-    private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM;
-    private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL
-            ? "BoundsAnimationController" : TAG_WM;
-    private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
-
-    private static final int DEFAULT_TRANSITION_DURATION = 425;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
-        SCHEDULE_PIP_MODE_CHANGED_ON_END})
-    public @interface SchedulePipModeChangedState {}
-    /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
-    public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
-    /** Schedule a PiP mode changed callback when this animation starts. */
-    public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
-    /** Schedule a PiP mode changed callback when this animation ends. */
-    public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
-
-    public static final int BOUNDS = 0;
-    public static final int FADE_IN = 1;
-
-    @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {}
-
-    private static final int FADE_IN_DURATION = 500;
-
-    // Only accessed on UI thread.
-    private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
-
-    private final class AppTransitionNotifier
-            extends WindowManagerInternal.AppTransitionListener implements Runnable {
-
-        public void onAppTransitionCancelledLocked() {
-            if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
-            animationFinished();
-        }
-        public void onAppTransitionFinishedLocked(IBinder token) {
-            if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
-            animationFinished();
-        }
-        private void animationFinished() {
-            if (mFinishAnimationAfterTransition) {
-                mHandler.removeCallbacks(this);
-                // This might end up calling into activity manager which will be bad since we have
-                // the window manager lock held at this point. Post a message to take care of the
-                // processing so we don't deadlock.
-                mHandler.post(this);
-            }
-        }
-
-        @Override
-        public void run() {
-            for (int i = 0; i < mRunningAnimations.size(); i++) {
-                final BoundsAnimator b = mRunningAnimations.valueAt(i);
-                b.onAnimationEnd(null);
-            }
-        }
-    }
-
-    private final Handler mHandler;
-    private final AppTransition mAppTransition;
-    private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
-    private final Interpolator mFastOutSlowInInterpolator;
-    private boolean mFinishAnimationAfterTransition = false;
-    private final AnimationHandler mAnimationHandler;
-    private Choreographer mChoreographer;
-    private @AnimationType int mAnimationType;
-
-    private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
-
-    BoundsAnimationController(Context context, AppTransition transition, Handler handler,
-            AnimationHandler animationHandler) {
-        mHandler = handler;
-        mAppTransition = transition;
-        mAppTransition.registerListenerLocked(mAppTransitionNotifier);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        mAnimationHandler = animationHandler;
-        if (animationHandler != null) {
-            // If an animation handler is provided, then ensure that it runs on the sf vsync tick
-            handler.post(() -> {
-                mChoreographer = Choreographer.getSfInstance();
-                animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
-            });
-        }
-    }
-
-    @VisibleForTesting
-    final class BoundsAnimator extends ValueAnimator
-            implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
-
-        private final BoundsAnimationTarget mTarget;
-        private final @AnimationType int mAnimationType;
-        private final Rect mFrom = new Rect();
-        private final Rect mTo = new Rect();
-        private final Rect mTmpRect = new Rect();
-        private final Rect mTmpTaskBounds = new Rect();
-
-        // True if this this animation was canceled and will be replaced the another animation from
-        // the same {@link #BoundsAnimationTarget} target.
-        private boolean mSkipFinalResize;
-        // True if this animation was canceled by the user, not as a part of a replacing animation
-        private boolean mSkipAnimationEnd;
-
-        // True if the animation target is animating from the fullscreen. Only one of
-        // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the
-        // animation.
-        private boolean mMoveFromFullscreen;
-        // True if the animation target should be moved to the fullscreen stack at the end of this
-        // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be
-        // true at any time in the animation.
-        private boolean mMoveToFullscreen;
-
-        // Whether to schedule PiP mode changes on animation start/end
-        private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
-        private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
-
-        // Depending on whether we are animating from
-        // a smaller to a larger size
-        private int mFrozenTaskWidth;
-        private int mFrozenTaskHeight;
-
-        // Timeout callback to ensure we continue the animation if waiting for resuming or app
-        // windows drawn fails
-        private final Runnable mResumeRunnable = () -> {
-            if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn");
-            resume();
-        };
-
-        // If this animator is explicitly cancelled when it's in paused state, we should not
-        // attempt to resume the animation. Use this flag to avoid such behavior.
-        private boolean mIsCancelled;
-
-        BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from,
-                Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState,
-                @SchedulePipModeChangedState int prevShedulePipModeChangedState,
-                boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) {
-            super();
-            mTarget = target;
-            mAnimationType = animationType;
-            mFrom.set(from);
-            mTo.set(to);
-            mSchedulePipModeChangedState = schedulePipModeChangedState;
-            mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
-            mMoveFromFullscreen = moveFromFullscreen;
-            mMoveToFullscreen = moveToFullscreen;
-            addUpdateListener(this);
-            addListener(this);
-
-            // If we are animating from smaller to larger, we want to change the task bounds
-            // to their final size immediately so we can use scaling to make the window
-            // larger. Likewise if we are going from bigger to smaller, we want to wait until
-            // the end so we don't have to upscale from the smaller finished size.
-            if (mAnimationType == BOUNDS) {
-                if (animatingToLargerSize()) {
-                    mFrozenTaskWidth = mTo.width();
-                    mFrozenTaskHeight = mTo.height();
-                } else {
-                    mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
-                    mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
-                }
-            }
-        }
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
-                    + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
-                    + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
-            mIsCancelled = false;
-            mFinishAnimationAfterTransition = false;
-            mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
-                    mFrom.top + mFrozenTaskHeight);
-
-            // Boost the thread priority of the animation thread while the bounds animation is
-            // running
-            updateBooster();
-
-            // Ensure that we have prepared the target for animation before we trigger any size
-            // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
-            // otherwise.
-            boolean continueAnimation;
-            if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
-                continueAnimation = mTarget.onAnimationStart(
-                        mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START,
-                        false /* forceUpdate */, mAnimationType);
-
-                // When starting an animation from fullscreen, pause here and wait for the
-                // windows-drawn signal before we start the rest of the transition down into PiP.
-                if (continueAnimation && mMoveFromFullscreen
-                        && mTarget.shouldDeferStartOnMoveToFullscreen()) {
-                    pause();
-                }
-            } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
-                    mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                // We are replacing a running animation into PiP, but since it hasn't completed, the
-                // client will not currently receive any picture-in-picture mode change callbacks.
-                // However, we still need to report to them that they are leaving PiP, so this will
-                // force an update via a mode changed callback.
-                continueAnimation = mTarget.onAnimationStart(
-                        true /* schedulePipModeChangedCallback */, true /* forceUpdate */,
-                        mAnimationType);
-            } else {
-                // The animation is already running, but we should check that the TaskStack is still
-                // valid before continuing with the animation
-                continueAnimation = mTarget.isAttached();
-            }
-
-            if (!continueAnimation) {
-                // No point of trying to animate something that isn't attached to the hierarchy
-                // anymore.
-                cancel();
-                return;
-            }
-
-            // Immediately update the task bounds if they have to become larger, but preserve
-            // 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();
-                }
-            }
-        }
-
-        @Override
-        public void pause() {
-            if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn");
-            super.pause();
-            mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
-        }
-
-        @Override
-        public void resume() {
-            if (DEBUG) Slog.d(TAG, "resume:");
-            mHandler.removeCallbacks(mResumeRunnable);
-            if (!mIsCancelled) super.resume();
-        }
-
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            final float value = (Float) animation.getAnimatedValue();
-            if (mAnimationType == FADE_IN) {
-                if (!mTarget.setPinnedStackAlpha(value)) {
-                    cancelAndCallAnimationEnd();
-                }
-                return;
-            }
-
-            final float remains = 1 - value;
-            mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
-            mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
-            mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
-            mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
-            if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
-                    + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
-                    + " remains=" + remains);
-
-            mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
-                    mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
-
-            if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
-                // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
-                // any further animation.
-                if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
-
-                // If we have already scheduled a PiP mode changed at the start of the animation,
-                // then we need to clean up and schedule one at the end, since we have canceled the
-                // animation to the final state.
-                if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-
-                // Since we are cancelling immediately without a replacement animation, send the
-                // animation end to maintain callback parity, but also skip any further resizes
-                cancelAndCallAnimationEnd();
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
-                    + " mSkipFinalResize=" + mSkipFinalResize
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
-                    + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
-                    + " callers=" + Debug.getCallers(2));
-
-            // There could be another animation running. For example in the
-            // move to fullscreen case, recents will also be closing while the
-            // previous task will be taking its place in the fullscreen stack.
-            // we have to ensure this is completed before we finish the animation
-            // and take our place in the fullscreen stack.
-            if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
-                mFinishAnimationAfterTransition = true;
-                return;
-            }
-
-            if (!mSkipAnimationEnd) {
-                // If this animation has already scheduled the picture-in-picture mode on start, and
-                // we are not skipping the final resize due to being canceled, then move the PiP to
-                // fullscreen once the animation ends
-                if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
-                        + " moveToFullscreen=" + mMoveToFullscreen);
-                mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
-                        SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
-                                mMoveToFullscreen);
-            }
-
-            // Clean up this animation
-            removeListener(this);
-            removeUpdateListener(this);
-            mRunningAnimations.remove(mTarget);
-
-            // Reset the thread priority of the animation thread after the bounds animation is done
-            updateBooster();
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mIsCancelled = true;
-            // Always skip the final resize when the animation is canceled
-            mSkipFinalResize = true;
-            mMoveToFullscreen = false;
-        }
-
-        private void cancelAndCallAnimationEnd() {
-            if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
-            mSkipAnimationEnd = false;
-            super.cancel();
-        }
-
-        @Override
-        public void cancel() {
-            if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
-            mSkipAnimationEnd = true;
-            super.cancel();
-
-            // Reset the thread priority of the animation thread if the bounds animation is canceled
-            updateBooster();
-        }
-
-        /**
-         * @return true if the animation target is the same as the input bounds.
-         */
-        boolean isAnimatingTo(Rect bounds) {
-            return mTo.equals(bounds);
-        }
-
-        /**
-         * @return true if we are animating to a larger surface size
-         */
-        @VisibleForTesting
-        boolean animatingToLargerSize() {
-            // TODO: Fix this check for aspect ratio changes
-            return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height());
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-            // Do nothing
-        }
-
-        @Override
-        public AnimationHandler getAnimationHandler() {
-            if (mAnimationHandler != null) {
-                return mAnimationHandler;
-            }
-            return super.getAnimationHandler();
-        }
-    }
-
-    public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
-            int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
-            boolean moveFromFullscreen, boolean moveToFullscreen,
-            @AnimationType int animationType) {
-        animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
-                moveFromFullscreen, moveToFullscreen, animationType);
-    }
-
-    /**
-     * Cancel existing animation if the destination was modified.
-     */
-    void cancel(final BoundsAnimationTarget target) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-        if (existing != null) {
-            // Cancel animation. Since its already started, send animation end to client.
-            if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target);
-            existing.cancelAndCallAnimationEnd();
-        }
-    }
-
-    @VisibleForTesting
-    BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
-            int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
-            boolean moveFromFullscreen, boolean moveToFullscreen,
-            @AnimationType int animationType) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-
-        if (isRunningFadeInAnimation(target) && from.width() == to.width()
-                && from.height() == to.height()) {
-            animationType = FADE_IN;
-        }
-        final boolean replacing = existing != null;
-        @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-
-        if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
-                + " schedulePipModeChangedState=" + schedulePipModeChangedState
-                + " replacing=" + replacing);
-
-        Rect frozenTask = new Rect();
-        if (replacing) {
-            if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
-                    && (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
-                // Just let the current animation complete if it has the same destination as the
-                // one we are trying to start, and, if moveTo/FromFullscreen was requested, already
-                // has that flag set.
-                if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as "
-                        + "existing=" + existing + ", ignoring...");
-                return existing;
-            }
-
-            // Save the previous state
-            prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
-
-            // Update the PiP callback states if we are replacing the animation
-            if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
-                            + " existing deferred state");
-                } else {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
-                            + " on start already processed, schedule deferred update on end");
-                    schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-            } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
-                if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
-                            + " callback on start will be processed");
-                } else {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
-                            + " existing deferred state");
-                    schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-            }
-
-            // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation
-            // specifies a direction.
-            if (!moveFromFullscreen && !moveToFullscreen) {
-                moveToFullscreen = existing.mMoveToFullscreen;
-                moveFromFullscreen = existing.mMoveFromFullscreen;
-            }
-
-            // We are in the middle of an existing animation, so that this new animation may
-            // start from an interpolated bounds. We should keep using the existing frozen task
-            // width/height for consistent configurations.
-            frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight);
-
-            // Since we are replacing, we skip both animation start and end callbacks
-            existing.cancel();
-        }
-        if (animationType == FADE_IN) {
-            target.setPinnedStackSize(to, null);
-        }
-
-        final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to,
-                schedulePipModeChangedState, prevSchedulePipModeChangedState,
-                moveFromFullscreen, moveToFullscreen, frozenTask);
-        mRunningAnimations.put(target, animator);
-        animator.setFloatValues(0f, 1f);
-        animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION
-                : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION)
-                        * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
-        animator.start();
-        return animator;
-    }
-
-    public void setAnimationType(@AnimationType int animationType) {
-        mAnimationType = animationType;
-    }
-
-    /** return the current animation type. */
-    public @AnimationType int getAnimationType() {
-        @AnimationType int animationType = mAnimationType;
-        // Default to BOUNDS.
-        mAnimationType = BOUNDS;
-        return animationType;
-    }
-
-    boolean isAnimationTypeFadeIn() {
-        return mAnimationType == FADE_IN;
-    }
-
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    public void onAllWindowsDrawn() {
-        if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:");
-        mHandler.post(this::resume);
-    }
-
-    private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-        return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted();
-    }
-
-    private void resume() {
-        for (int i = 0; i < mRunningAnimations.size(); i++) {
-            final BoundsAnimator b = mRunningAnimations.valueAt(i);
-            b.resume();
-        }
-    }
-
-    private void updateBooster() {
-        WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning(
-                !mRunningAnimations.isEmpty());
-    }
-}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
deleted file mode 100644
index b1d5359..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.graphics.Rect;
-
-/**
- * The target for a BoundsAnimation.
- * @see BoundsAnimationController
- */
-interface BoundsAnimationTarget {
-
-    /**
-     * Callback for the target to inform it that the animation has started, so it can do some
-     * necessary preparation.
-     *
-     * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
-     * callbacks
-     * @return whether to continue the animation
-     */
-    boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
-            @BoundsAnimationController.AnimationType int animationType);
-
-    /**
-     * @return Whether the animation should be paused waiting for the windows to draw before
-     *         entering PiP.
-     */
-    boolean shouldDeferStartOnMoveToFullscreen();
-
-    /**
-     * Sets the size of the target (without any intermediate steps, like scheduling animation)
-     * but freezes the bounds of any tasks in the target at taskBounds, to allow for more
-     * flexibility during resizing. Only works for the pinned stack at the moment.  This will
-     * only be called between onAnimationStart() and onAnimationEnd().
-     *
-     * @return Whether the target should continue to be animated and this call was successful.
-     * If false, the animation will be cancelled because the user has determined that the
-     * animation is now invalid and not required. In such a case, the cancel will trigger the
-     * animation end callback as well, but will not send any further size changes.
-     */
-    boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds);
-
-    /** Sets the alpha of the animation target */
-    boolean setPinnedStackAlpha(float alpha);
-
-    /**
-     * Callback for the target to inform it that the animation has ended, so it can do some
-     * necessary cleanup.
-     *
-     * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
-     * callbacks
-     * @param finalStackSize the final stack bounds to set on the target (can be to indicate that
-     * the animation was cancelled and the target does not need to update to the final stack bounds)
-     * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack
-     * when the animation completes
-     */
-    void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
-            boolean moveToFullscreen);
-
-    /** @return True if the target is attached to the window hierarchy. */
-    default boolean isAttached() {
-        return true;
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0029dc8..8ccb59f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -150,7 +150,6 @@
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
 
-import android.animation.AnimationHandler;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -317,7 +316,6 @@
     final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
     final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
     final UnknownAppVisibilityController mUnknownAppVisibilityController;
-    BoundsAnimationController mBoundsAnimationController;
 
     private MetricsLogger mMetricsLogger;
 
@@ -977,10 +975,6 @@
         mAppTransitionController = new AppTransitionController(mWmService, this);
         mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
 
-        AnimationHandler animationHandler = new AnimationHandler();
-        mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext,
-                mAppTransition, mWmService.mAnimationHandler, animationHandler);
-
         final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
                 "PointerEventDispatcher" + mDisplayId, mDisplayId);
         mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 668b609..e14b8ae 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -52,7 +50,7 @@
  * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
  *    and IME state into account.
  * 2) When rotating the device: the controller calculates the new bounds in the new orientation,
- *    taking the minimized and IME state into account. In this case, we currently ignore the
+ *    taking the IME state into account. In this case, we currently ignore the
  *    SystemUI adjustments (ie. expanded for menu, interaction, etc).
  *
  * Other changes in the system, including adjustment of IME, configuration change, and more are
@@ -72,8 +70,6 @@
 
     private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
 
-    // States that affect how the PIP can be manipulated
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
 
@@ -84,9 +80,6 @@
     // Used to calculate stack bounds across rotations
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
 
-    // The size and position information that describes where the pinned stack will go by default.
-    private float mDefaultAspectRatio;
-
     // The aspect ratio bounds of the PIP.
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
@@ -98,43 +91,12 @@
      * The callback object passed to listeners for them to notify the controller of state changes.
      */
     private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
-
-        @Override
-        public void setIsMinimized(final boolean isMinimized) {
-            mHandler.post(() -> {
-                mIsMinimized = isMinimized;
-            });
-        }
-
         @Override
         public int getDisplayRotation() {
             synchronized (mService.mGlobalLock) {
                 return mDisplayInfo.rotation;
             }
         }
-
-        @Override
-        public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
-                int animationDuration) {
-            synchronized (mService.mGlobalLock) {
-                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
-                pinnedStack.animateResizePinnedStack(destinationBounds,
-                        sourceRectHint, animationDuration, true /* fromFullscreen */);
-            }
-        }
-
-        @Override
-        public void resetBoundsAnimation(Rect bounds) {
-            synchronized (mService.mGlobalLock) {
-                if (mDisplayContent.hasPinnedTask()) {
-                    final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode(
-                            WINDOWING_MODE_PINNED);
-                    if (pinnedStack != null) {
-                        pinnedStack.resetCurrentBoundsAnimation(bounds);
-                    }
-                }
-            }
-        }
     }
 
     /**
@@ -157,10 +119,6 @@
         mDisplayContent = displayContent;
         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
         reloadResources();
-        // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
-        // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
-        // triggers a configuration change and the resources to be reloaded.
-        mAspectRatio = mDefaultAspectRatio;
     }
 
     void onConfigurationChanged() {
@@ -172,8 +130,6 @@
      */
     private void reloadResources() {
         final Resources res = mService.mContext.getResources();
-        mDefaultAspectRatio = res.getFloat(
-                com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
         mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
         mMinAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
@@ -191,12 +147,8 @@
             mPinnedStackListener = listener;
             notifyDisplayInfoChanged(mDisplayInfo);
             notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
-            // The movement bounds notification needs to be sent before the minimized state, since
-            // SystemUI may use the bounds to restore the minimized position
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
             notifyActionsChanged(mActions);
-            notifyMinimizeChanged(mIsMinimized);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -247,8 +199,7 @@
     void onDisplayInfoChanged(DisplayInfo displayInfo) {
         synchronized (mService.mGlobalLock) {
             setDisplayInfo(displayInfo);
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
         }
     }
 
@@ -269,7 +220,7 @@
         mIsImeShowing = imeShowing;
         mImeHeight = imeHeight;
         notifyImeVisibilityChanged(imeShowing, imeHeight);
-        notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+        notifyMovementBoundsChanged(true /* fromImeAdjustment */);
     }
 
     /**
@@ -279,10 +230,7 @@
         if (Float.compare(mAspectRatio, aspectRatio) != 0) {
             mAspectRatio = aspectRatio;
             notifyAspectRatioChanged(aspectRatio);
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
-            notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio,
-                    null /* stackBounds */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
         }
     }
 
@@ -304,10 +252,6 @@
         notifyActionsChanged(mActions);
     }
 
-    void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
-        notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
-    }
-
     /**
      * Notifies listeners that the PIP needs to be adjusted for the IME.
      */
@@ -331,19 +275,6 @@
     }
 
     /**
-     * Notifies listeners that the PIP minimized state has changed.
-     */
-    private void notifyMinimizeChanged(boolean isMinimized) {
-        if (mPinnedStackListener != null) {
-            try {
-                mPinnedStackListener.onMinimizedStateChanged(isMinimized);
-            } catch (RemoteException e) {
-                Slog.e(TAG_WM, "Error delivering minimize changed event.", e);
-            }
-        }
-    }
-
-    /**
      * Notifies listeners that the PIP actions have changed.
      */
     private void notifyActionsChanged(List<RemoteAction> actions) {
@@ -359,8 +290,7 @@
     /**
      * Notifies listeners that the PIP movement bounds have changed.
      */
-    private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
-            boolean fromShelfAdjustment) {
+    private void notifyMovementBoundsChanged(boolean fromImeAdjustment) {
         synchronized (mService.mGlobalLock) {
             if (mPinnedStackListener == null) {
                 return;
@@ -371,8 +301,7 @@
                 if (pinnedStack != null) {
                     pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
                 }
-                mPinnedStackListener.onMovementBoundsChanged(animatingBounds,
-                        fromImeAdjustment, fromShelfAdjustment);
+                mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
@@ -391,24 +320,10 @@
         }
     }
 
-    /**
-     * Notifies listeners that the PIP animation is about to happen.
-     */
-    private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
-        if (mPinnedStackListener == null) return;
-        try {
-            mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
-        } catch (RemoteException e) {
-            Slog.e(TAG_WM, "Error delivering prepare animation event.", e);
-        }
-    }
-
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "PinnedStackController");
-        pw.println(prefix + "  mDefaultAspectRatio=" + mDefaultAspectRatio);
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mImeHeight=" + mImeHeight);
-        pw.println(prefix + "  mIsMinimized=" + mIsMinimized);
         pw.println(prefix + "  mAspectRatio=" + mAspectRatio);
         pw.println(prefix + "  mMinAspectRatio=" + mMinAspectRatio);
         pw.println(prefix + "  mMaxAspectRatio=" + mMaxAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index e92bbaa..adafdec 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -26,8 +26,6 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -426,11 +424,6 @@
             return;
         }
 
-        final DisplayContent dc =
-                mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
-        dc.mBoundsAnimationController.setAnimationType(
-                controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS);
-
         // We defer canceling the recents animation until the next app transition in the following
         // cases:
         // 1) The next launching task is not being animated by the recents animation
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6b6b946..4399d65 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -26,7 +26,6 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -231,7 +230,6 @@
                 mCallbacks.onAnimationFinished(moveHomeToTop
                         ? REORDER_MOVE_TO_TOP
                         : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
-                mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -309,14 +307,6 @@
         }
 
         @Override
-        @Deprecated
-        public void setCancelWithDeferredScreenshot(boolean screenshot) {
-            synchronized (mService.mGlobalLock) {
-                setDeferredCancel(true /* deferred */, screenshot);
-            }
-        }
-
-        @Override
         public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
             synchronized (mService.mGlobalLock) {
                 setDeferredCancel(defer, screenshot);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index aa6bdfd..64d7db2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2149,19 +2149,6 @@
             mService.continueWindowLayout();
         }
 
-        // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
-        // Notify the pinned stack controller to prepare the PiP animation, expect callback
-        // delivered from SystemUI to WM to start the animation. Unless we are using
-        // the TaskOrganizer in which case the animation will be entirely handled
-        // on that side.
-        if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED)
-                == null) {
-            final PinnedStackController pinnedStackController =
-                display.mDisplayContent.getPinnedStackController();
-            pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
-                    null /* stackBounds */);
-        }
-
         // TODO: revisit the following statement after the animation is moved from WM to SysUI.
         // Update the visibility of all activities after the they have been reparented to the new
         // stack.  This MUST run after the animation above is scheduled to ensure that the windows
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ecfac1b..76805e9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -111,7 +111,6 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Icon;
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -120,7 +119,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
-import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.view.DisplayInfo;
@@ -2910,8 +2908,7 @@
      * we will have a jump at the end.
      */
     boolean isFloating() {
-        return getWindowConfiguration().tasksAreFloating()
-                && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
+        return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index f715d8f..96a9127 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -37,7 +37,6 @@
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
     private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
     private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
     private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
     private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
     private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
@@ -48,7 +47,6 @@
     private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
     private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
     private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
     private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
@@ -124,14 +122,6 @@
         l.onPinnedActivityRestartAttempt(m.arg1 != 0);
     };
 
-    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
-        l.onPinnedStackAnimationStarted();
-    };
-
-    private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
-        l.onPinnedStackAnimationEnded();
-    };
-
     private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
         l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
     };
@@ -233,12 +223,6 @@
                 case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
                     break;
-                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
-                    break;
-                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
-                    break;
                 case NOTIFY_FORCED_RESIZABLE_MSG:
                     forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
                     break;
@@ -386,24 +370,6 @@
         msg.sendToTarget();
     }
 
-    /** Notifies all listeners when the pinned stack animation starts. */
-    void notifyPinnedStackAnimationStarted() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
-        msg.sendToTarget();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    void notifyPinnedStackAnimationEnded() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
-        msg.sendToTarget();
-    }
-
     void notifyActivityDismissingDockedStack() {
         mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
         final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6caa27c..0f1e623 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -306,7 +306,8 @@
         task.fillTaskInfo(mTmpTaskInfo);
         boolean changed = lastInfo == null
                 || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
-                || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
+                || mTmpTaskInfo.isResizable() != lastInfo.isResizable()
+                || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams;
         if (!(changed || force)) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b250083..37597fb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1078,15 +1078,6 @@
             }
 
             final ActivityStack stack = getRootTask();
-            if (inPinnedWindowingMode() && stack != null
-                    && stack.lastAnimatingBoundsWasToFullscreen()) {
-                // PIP edge case: When going from pinned to fullscreen, we apply a
-                // tempInsetFrame for the full task - but we're still at the start of the animation.
-                // To prevent a jump if there's a letterbox, restrict to the parent frame.
-                mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
-                windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
-            }
-
             layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame);
             windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
             layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
@@ -1343,16 +1334,6 @@
             return;
         }
 
-        final Task task = getTask();
-        // In the case of stack bound animations, the window frames will update (unlike other
-        // animations which just modify various transformation properties). We don't want to
-        // notify the client of frame changes in this case. Not only is it a lot of churn, but
-        // the frame may not correspond to the surface size or the onscreen area at various
-        // phases in the animation, and the client will become sad and confused.
-        if (task != null && task.getStack().isAnimatingBounds()) {
-            return;
-        }
-
         boolean didFrameInsetsChange = setReportResizeHints();
         boolean configChanged = !isLastConfigReportedToClient();
         if (DEBUG_CONFIGURATION && configChanged) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a1a9af6..81d0e3e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -902,15 +902,9 @@
             boolean allowStretching = false;
             task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
             // If we don't have source bounds, we can attempt to use the content insets
-            // in the following scenario:
-            //    1. We have content insets.
-            //    2. We are not transitioning to full screen
-            // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than
-            // the mBoundsAnimating state, as we may have already left it and only be here
-            // because of the force-scale until resize state.
+            // if we have content insets.
             if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0
-                    || mWin.mLastRelayoutContentInsets.height() > 0)
-                        && !task.getStack().lastAnimatingBoundsWasToFullscreen()) {
+                    || mWin.mLastRelayoutContentInsets.height() > 0)) {
                 mTmpSourceBounds.set(task.getStack().mPreAnimationBounds);
                 mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets);
                 allowStretching = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b917e1b..4fe6d78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -31,7 +31,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 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.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -50,7 +49,6 @@
 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.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -139,39 +137,6 @@
     }
 
     @Test
-    public void testUpdateLaunchBounds() {
-        // When in a non-resizeable stack, the task bounds should be updated.
-        final Task task = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        final Rect bounds = new Rect(10, 10, 100, 100);
-
-        mStarter.updateBounds(task, bounds);
-        assertEquals(bounds, task.getRequestedOverrideBounds());
-        assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds());
-
-        // When in a resizeable stack, the stack bounds should be updated as well.
-        final Task task2 = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
-        mStarter.updateBounds(task2, bounds);
-
-        verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()),
-                eq(bounds), anyInt());
-
-        // In the case of no animation, the stack and task bounds should be set immediately.
-        if (!ANIMATE) {
-            assertEquals(bounds, task2.getStack().getRequestedOverrideBounds());
-            assertEquals(bounds, task2.getRequestedOverrideBounds());
-        } else {
-            assertEquals(new Rect(), task2.getRequestedOverrideBounds());
-        }
-    }
-
-    @Test
     public void testStartActivityPreconditions() {
         verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
         verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
deleted file mode 100644
index 1dda535..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * Copyright (C) 2017 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
-import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
- * depending on the various interactions.
- *
- * We are really concerned about only three of the transition states [F = fullscreen, !F = floating]
- * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition,
- * or if a new animation starts on the same target.  The tests below verifies that the target is
- * notified of all the cases where it is animating and cancelled so that it can respond
- * appropriately.
- *
- * Build/Install/Run:
- *  atest WmTests:BoundsAnimationControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class BoundsAnimationControllerTests extends WindowTestsBase {
-
-    /**
-     * Mock value animator to simulate updates with.
-     */
-    private static class MockValueAnimator extends ValueAnimator {
-
-        private float mFraction;
-
-        MockValueAnimator getWithValue(float fraction) {
-            mFraction = fraction;
-            return this;
-        }
-
-        @Override
-        public Object getAnimatedValue() {
-            return mFraction;
-        }
-    }
-
-    /**
-     * Mock app transition to fire notifications to the bounds animator.
-     */
-    private static class MockAppTransition extends AppTransition {
-
-        private AppTransitionListener mListener;
-
-        MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) {
-            super(context, wm, displayContent);
-        }
-
-        @Override
-        void registerListenerLocked(AppTransitionListener listener) {
-            mListener = listener;
-        }
-
-        public void notifyTransitionPending() {
-            mListener.onAppTransitionPendingLocked();
-        }
-
-        public void notifyTransitionCancelled(int transit) {
-            mListener.onAppTransitionCancelledLocked(transit);
-        }
-
-        public void notifyTransitionStarting(int transit) {
-            mListener.onAppTransitionStartingLocked(transit, 0, 0, 0);
-        }
-
-        public void notifyTransitionFinished() {
-            mListener.onAppTransitionFinishedLocked(null);
-        }
-    }
-
-    /**
-     * A test animate bounds user to track callbacks from the bounds animation.
-     */
-    private static class TestBoundsAnimationTarget implements BoundsAnimationTarget {
-
-        boolean mAwaitingAnimationStart;
-        boolean mMovedToFullscreen;
-        boolean mAnimationStarted;
-        boolean mSchedulePipModeChangedOnStart;
-        boolean mForcePipModeChangedCallback;
-        boolean mAnimationEnded;
-        Rect mAnimationEndFinalStackBounds;
-        boolean mSchedulePipModeChangedOnEnd;
-        boolean mBoundsUpdated;
-        boolean mCancelRequested;
-        Rect mStackBounds;
-        Rect mTaskBounds;
-        float mAlpha;
-        @BoundsAnimationController.AnimationType int mAnimationType;
-
-        void initialize(Rect from) {
-            mAwaitingAnimationStart = true;
-            mMovedToFullscreen = false;
-            mAnimationStarted = false;
-            mAnimationEnded = false;
-            mAnimationEndFinalStackBounds = null;
-            mForcePipModeChangedCallback = false;
-            mSchedulePipModeChangedOnStart = false;
-            mSchedulePipModeChangedOnEnd = false;
-            mStackBounds = from;
-            mTaskBounds = null;
-            mBoundsUpdated = false;
-        }
-
-        @Override
-        public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
-                boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) {
-            mAwaitingAnimationStart = false;
-            mAnimationStarted = true;
-            mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
-            mForcePipModeChangedCallback = forceUpdate;
-            mAnimationType = animationType;
-            return true;
-        }
-
-        @Override
-        public boolean shouldDeferStartOnMoveToFullscreen() {
-            return true;
-        }
-
-        @Override
-        public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
-            // TODO: Once we break the runs apart, we should fail() here if this is called outside
-            //       of onAnimationStart() and onAnimationEnd()
-            if (mCancelRequested) {
-                mCancelRequested = false;
-                return false;
-            } else {
-                mBoundsUpdated = true;
-                mStackBounds = stackBounds;
-                mTaskBounds = taskBounds;
-                return true;
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds,
-                boolean moveToFullscreen) {
-            mAnimationEnded = true;
-            mAnimationEndFinalStackBounds = finalStackBounds;
-            mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback;
-            mMovedToFullscreen = moveToFullscreen;
-            mTaskBounds = null;
-        }
-
-        @Override
-        public boolean setPinnedStackAlpha(float alpha) {
-            mAlpha = alpha;
-            return true;
-        }
-    }
-
-    /**
-     * Drives the animations, makes common assertions along the way.
-     */
-    private static class BoundsAnimationDriver {
-
-        private final BoundsAnimationController mController;
-        private final TestBoundsAnimationTarget mTarget;
-        private final MockValueAnimator mMockAnimator;
-
-        private BoundsAnimator mAnimator;
-        private Rect mFrom;
-        private Rect mTo;
-        private Rect mLargerBounds;
-        private Rect mExpectedFinalBounds;
-        private @BoundsAnimationController.AnimationType int mAnimationType;
-
-        BoundsAnimationDriver(BoundsAnimationController controller,
-                TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
-            mController = controller;
-            mTarget = target;
-            mMockAnimator = mockValueAnimator;
-        }
-
-        BoundsAnimationDriver start(Rect from, Rect to,
-                @BoundsAnimationController.AnimationType int animationType) {
-            if (mAnimator != null) {
-                throw new IllegalArgumentException("Call restart() to restart an animation");
-            }
-
-            boolean fromFullscreen = from.equals(BOUNDS_FULL);
-            boolean toFullscreen = to.equals(BOUNDS_FULL);
-
-            mTarget.initialize(from);
-
-            // Started, not running
-            assertTrue(mTarget.mAwaitingAnimationStart);
-            assertFalse(mTarget.mAnimationStarted);
-
-            startImpl(from, to, animationType);
-
-            // Ensure that the animator is paused for the all windows drawn signal when animating
-            // to/from fullscreen
-            if (fromFullscreen || toFullscreen) {
-                assertTrue(mAnimator.isPaused());
-                mController.onAllWindowsDrawn();
-            } else {
-                assertTrue(!mAnimator.isPaused());
-            }
-
-            // Started and running
-            assertFalse(mTarget.mAwaitingAnimationStart);
-            assertTrue(mTarget.mAnimationStarted);
-
-            return this;
-        }
-
-        BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
-            if (mAnimator == null) {
-                throw new IllegalArgumentException("Call start() to start a new animation");
-            }
-
-            BoundsAnimator oldAnimator = mAnimator;
-            boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo);
-
-            // Reset the animation start state
-            mTarget.mAnimationStarted = false;
-
-            // Start animation
-            startImpl(mTarget.mStackBounds, to, BOUNDS);
-
-            if (toSameBounds) {
-                // Same animator if same final bounds
-                assertSame(oldAnimator, mAnimator);
-            }
-
-            if (expectStartedAndPipModeChangedCallback) {
-                // Replacing animation with pending pip mode changed callback, ensure we update
-                assertTrue(mTarget.mAnimationStarted);
-                assertTrue(mTarget.mSchedulePipModeChangedOnStart);
-                assertTrue(mTarget.mForcePipModeChangedCallback);
-            } else {
-                // No animation start for replacing animation
-                assertFalse(mTarget.mAnimationStarted);
-            }
-            mTarget.mAnimationStarted = true;
-            return this;
-        }
-
-        private BoundsAnimationDriver startImpl(Rect from, Rect to,
-                @BoundsAnimationController.AnimationType int animationType) {
-            boolean fromFullscreen = from.equals(BOUNDS_FULL);
-            boolean toFullscreen = to.equals(BOUNDS_FULL);
-            mFrom = new Rect(from);
-            mTo = new Rect(to);
-            mExpectedFinalBounds = new Rect(to);
-            mLargerBounds = getLargerBounds(mFrom, mTo);
-            mAnimationType = animationType;
-
-            // Start animation
-            final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen
-                    ? SCHEDULE_PIP_MODE_CHANGED_ON_START
-                    : fromFullscreen
-                            ? SCHEDULE_PIP_MODE_CHANGED_ON_END
-                            : NO_PIP_MODE_CHANGED_CALLBACKS;
-            mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION,
-                    schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType);
-
-            if (animationType == BOUNDS) {
-                // Original stack bounds, frozen task bounds
-                assertEquals(mFrom, mTarget.mStackBounds);
-                assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
-
-                // Animating to larger size
-                if (mFrom.equals(mLargerBounds)) {
-                    assertFalse(mAnimator.animatingToLargerSize());
-                } else if (mTo.equals(mLargerBounds)) {
-                    assertTrue(mAnimator.animatingToLargerSize());
-                }
-            }
-
-            return this;
-        }
-
-        BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) {
-            // Callback made
-            assertTrue(mTarget.mAnimationStarted);
-
-            assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart);
-            return this;
-        }
-
-        BoundsAnimationDriver update(float t) {
-            mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t));
-
-            if (mAnimationType == BOUNDS) {
-                // Temporary stack bounds, frozen task bounds
-                if (t == 0f) {
-                    assertEquals(mFrom, mTarget.mStackBounds);
-                } else if (t == 1f) {
-                    assertEquals(mTo, mTarget.mStackBounds);
-                } else {
-                    assertNotEquals(mFrom, mTarget.mStackBounds);
-                    assertNotEquals(mTo, mTarget.mStackBounds);
-                }
-                assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
-            } else {
-                assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f);
-            }
-            return this;
-        }
-
-        BoundsAnimationDriver cancel() {
-            // Cancel
-            mTarget.mCancelRequested = true;
-            mTarget.mBoundsUpdated = false;
-            mExpectedFinalBounds = null;
-
-            // Update
-            mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
-
-            // Not started, not running, cancel reset
-            assertFalse(mTarget.mCancelRequested);
-
-            // Stack/task bounds not updated
-            assertFalse(mTarget.mBoundsUpdated);
-
-            // Callback made
-            assertTrue(mTarget.mAnimationEnded);
-            assertNull(mTarget.mAnimationEndFinalStackBounds);
-
-            return this;
-        }
-
-        BoundsAnimationDriver end() {
-            mAnimator.end();
-
-            if (mAnimationType == BOUNDS) {
-                // Final stack bounds
-                assertEquals(mTo, mTarget.mStackBounds);
-                assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
-                assertNull(mTarget.mTaskBounds);
-            } else {
-                assertEquals(mTarget.mAlpha, 1f, 0.01f);
-            }
-
-            return this;
-        }
-
-        BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged,
-                boolean moveToFullscreen) {
-            // Callback made
-            assertTrue(mTarget.mAnimationEnded);
-
-            assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd);
-            assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen);
-            return this;
-        }
-
-        private static Rect getLargerBounds(Rect r1, Rect r2) {
-            int r1Area = r1.width() * r1.height();
-            int r2Area = r2.width() * r2.height();
-            return (r1Area <= r2Area) ? r2 : r1;
-        }
-    }
-
-    // Constants
-    private static final boolean SCHEDULE_PIP_MODE_CHANGED = true;
-    private static final boolean MOVE_TO_FULLSCREEN = true;
-    private static final int DURATION = 100;
-
-    // Some dummy bounds to represent fullscreen and floating bounds
-    private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
-    private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95);
-    private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95);
-
-    // Common
-    private MockAppTransition mMockAppTransition;
-    private TestBoundsAnimationTarget mTarget;
-    private BoundsAnimationController mController;
-    private BoundsAnimationDriver mDriver;
-
-    // Temp
-    private static final Rect sTmpRect = new Rect();
-
-    @Before
-    public void setUp() throws Exception {
-        final Context context = getInstrumentation().getTargetContext();
-        final Handler handler = new Handler(Looper.getMainLooper());
-        mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent);
-        mTarget = new TestBoundsAnimationTarget();
-        mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
-        final MockValueAnimator mockValueAnimator = new MockValueAnimator();
-        mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator);
-    }
-
-    /** BASE TRANSITIONS **/
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingTransition() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenTransition() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToSmallerFloatingTransition() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToLargerFloatingTransition() {
-        mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    /** F->!F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING,
-                        false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
-        // When animating from fullscreen and the animation is interruped, we expect the animation
-        // start callback to be made, with a forced pip mode change callback
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    /** !F->F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromTarget() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING,
-                        false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    /** !F->!F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToSmallerFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToLargerFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFadeIn() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    /** MISC **/
-
-    @UiThreadTest
-    @Test
-    public void testBoundsAreCopied() {
-        Rect from = new Rect(0, 0, 100, 100);
-        Rect to = new Rect(25, 25, 75, 75);
-        mDriver.start(from, to, BOUNDS)
-                .update(0.25f)
-                .end();
-        assertEquals(new Rect(0, 0, 100, 100), from);
-        assertEquals(new Rect(25, 25, 75, 75), to);
-    }
-
-    /**
-     * @return whether the task and stack bounds would be the same if they were at the same offset.
-     */
-    private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
-        sTmpRect.set(taskBounds);
-        sTmpRect.offsetTo(stackBounds.left, stackBounds.top);
-        return stackBounds.equals(sTmpRect);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4449069..d9c5c4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1102,12 +1102,8 @@
         assertSecurityException(expectCallable,
                 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable,
-                () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1));
-        assertSecurityException(expectCallable,
                 () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
                         new Rect()));
-        assertSecurityException(expectCallable,
-                () -> mService.resizePinnedStack(new Rect(), new Rect()));
         assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
         assertSecurityException(expectCallable,
                 () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index c9efb70..5c36f5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -333,7 +333,7 @@
         assertTrue(mController.isAnimatingTask(activity.getTask()));
 
         // Assume activity transition should animate when no
-        // IRecentsAnimationController#setCancelWithDeferredScreenshot called.
+        // IRecentsAnimationController#setDeferCancelUntilNextTransition called.
         assertFalse(mController.shouldDeferCancelWithScreenshot());
         assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index b78107e..b3a25302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -208,7 +208,6 @@
     @Test
     public void testSetLaunchTaskBehindOfTargetActivity() {
         DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
         ActivityStack homeStack = display.getRootHomeTask();
         // Assume the home activity support recents.
         ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
@@ -254,7 +253,6 @@
     @Test
     public void testCancelAnimationOnVisibleStackOrderChange() {
         DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay();
-        display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
         ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mService)