Merge "Ensure we use the fullscreen stack bounds when expanding PiP."
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a3326b..30b267f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7694,7 +7694,7 @@
                             aspectRatio);
                     mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
                             bounds, true /* moveHomeStackToFront */);
-                    final ActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+                    final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
                     stack.setPictureInPictureAspectRatio(aspectRatio);
                     stack.setPictureInPictureActions(actions);
 
@@ -7748,9 +7748,9 @@
 
                 // Only update the saved args from the args that are set
                 r.pictureInPictureArgs.copyOnlySet(args);
-                final ActivityStack stack = r.getStack();
-                if (stack.getStackId() == PINNED_STACK_ID) {
+                if (r.getStack().getStackId() == PINNED_STACK_ID) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
+                    final PinnedActivityStack stack = r.getStack();
                     stack.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio());
                     stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
@@ -10312,7 +10312,7 @@
             synchronized (this) {
                 if (animate) {
                     if (stackId == PINNED_STACK_ID) {
-                        final ActivityStack pinnedStack =
+                        final PinnedActivityStack pinnedStack =
                                 mStackSupervisor.getStack(PINNED_STACK_ID);
                         pinnedStack.animateResizePinnedStack(bounds, animationDuration);
                     } else {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6f89cf2..aef429e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -880,8 +880,8 @@
     /**
      * @return Stack value from current task, null if there is no task.
      */
-    ActivityStack getStack() {
-        return task != null ? task.getStack() : null;
+    <T extends ActivityStack> T getStack() {
+        return task != null ? (T) task.getStack() : null;
     }
 
     boolean changeWindowTranslucency(boolean toOpaque) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b948c15..7f7caff 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -84,7 +84,6 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
-import android.app.RemoteAction;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -135,7 +134,8 @@
 /**
  * State and management of a single stack of activities.
  */
-final class ActivityStack extends ConfigurationContainer implements StackWindowListener {
+class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
+        implements StackWindowListener {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -247,7 +247,7 @@
 
     final ActivityManagerService mService;
     private final WindowManagerService mWindowManager;
-    private StackWindowController mWindowContainerController;
+    T mWindowContainerController;
     private final RecentTasks mRecentTasks;
 
     /**
@@ -462,14 +462,18 @@
                 ? new LaunchingTaskPositioner() : null;
         final ActivityStackSupervisor.ActivityDisplay display = mActivityContainer.mActivityDisplay;
         mTmpRect2.setEmpty();
-        mWindowContainerController = new StackWindowController(mStackId, this,
-                display.mDisplayId, onTop, mTmpRect2);
+        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
+                mTmpRect2);
         activityContainer.mStack = this;
         mStackSupervisor.mActivityContainers.put(mStackId, activityContainer);
         postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
-    StackWindowController getWindowContainerController() {
+    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+    }
+
+    T getWindowContainerController() {
         return mWindowContainerController;
     }
 
@@ -540,18 +544,6 @@
         mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
     }
 
-    void animateResizePinnedStack(Rect bounds, int animationDuration) {
-        mWindowContainerController.animateResizePinnedStack(bounds, animationDuration);
-    }
-
-    void setPictureInPictureAspectRatio(float aspectRatio) {
-        mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
-    }
-
-    void setPictureInPictureActions(List<RemoteAction> actions) {
-        mWindowContainerController.setPictureInPictureActions(actions);
-    }
-
     void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
             boolean ignoreVisibility) {
         mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7ceeff8..1e33bb9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -120,7 +120,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -153,7 +152,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Slog;
@@ -2037,19 +2035,20 @@
                 || mService.mSupportsFreeformWindowManagement;
     }
 
-    ActivityStack getStack(int stackId) {
+    protected <T extends ActivityStack> T getStack(int stackId) {
         return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
     }
 
-    ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
+    protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
+            boolean createOnTop) {
         final ActivityContainer activityContainer = mActivityContainers.get(stackId);
         if (activityContainer != null) {
-            return activityContainer.mStack;
+            return (T) activityContainer.mStack;
         }
         if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
             return null;
         }
-        return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+        return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
     }
 
     /**
@@ -2856,7 +2855,7 @@
 
         mWindowManager.deferSurfaceLayout();
         // Need to make sure the pinned stack exist so we can resize it below...
-        final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
         try {
             final TaskRecord task = r.task;
@@ -2906,7 +2905,7 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        stack.animateResizePinnedStack(bounds, -1);
+        stack.animateResizePinnedStack(bounds, -1 /* animationDuration */);
         mService.mTaskChangeNotificationController.notifyActivityPinned();
     }
 
@@ -4367,7 +4366,14 @@
             synchronized (mService) {
                 mStackId = stackId;
                 mActivityDisplay = activityDisplay;
-                new ActivityStack(this, mRecentTasks, onTop);
+                switch (mStackId) {
+                    case PINNED_STACK_ID:
+                        new PinnedActivityStack(this, mRecentTasks, onTop);
+                        break;
+                    default:
+                        new ActivityStack(this, mRecentTasks, onTop);
+                        break;
+                }
                 mIdString = "ActivtyContainer{" + mStackId + "}";
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
             }
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
new file mode 100644
index 0000000..aa7ab15
--- /dev/null
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -0,0 +1,55 @@
+/*
+ * 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.am;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.StackWindowController;
+
+import java.util.List;
+
+/**
+ * State and management of the pinned stack of activities.
+ */
+class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
+
+    PinnedActivityStack(ActivityContainer activityContainer,
+            RecentTasks recentTasks, boolean onTop) {
+        super(activityContainer, recentTasks, onTop);
+    }
+
+    @Override
+    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
+            Rect outBounds) {
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+    }
+
+    void animateResizePinnedStack(Rect bounds, int animationDuration) {
+        getWindowContainerController().animateResizePinnedStack(bounds, animationDuration);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        getWindowContainerController().setPictureInPictureActions(actions);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 01bd86d..837b69e 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -272,18 +272,10 @@
         void onAnimationEnd();
 
         void moveToFullscreen();
-
-        void getFullScreenBounds(Rect bounds);
     }
 
-    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration) {
-        boolean moveToFullscreen = false;
-        if (to == null) {
-            to = new Rect();
-            target.getFullScreenBounds(to);
-            moveToFullscreen = true;
-        }
-
+    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration,
+            boolean moveToFullscreen) {
         final BoundsAnimator existing = mRunningAnimations.get(target);
         final boolean replacing = existing != null;
 
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index cfeb198..5f41187 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -230,28 +230,6 @@
     }
 
     /**
-     * @return the movement bounds for the given {@param stackBounds} and the current state of the
-     *         controller.
-     */
-    private Rect getMovementBounds(Rect stackBounds) {
-        return getMovementBounds(stackBounds, true /* adjustForIme */);
-    }
-
-    /**
-     * @return the movement bounds for the given {@param stackBounds} and the current state of the
-     *         controller.
-     */
-    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
-        final Rect movementBounds = new Rect();
-        getInsetBounds(movementBounds);
-
-        // Apply the movement bounds adjustments based on the current state
-        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
-                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
-        return movementBounds;
-    }
-
-    /**
      * @param preChangeTargetBounds The final bounds of the stack if it is currently animating
      * @return the repositioned PIP bounds given it's pre-change bounds, and the new display
      *         content.
@@ -387,6 +365,28 @@
     }
 
     /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds) {
+        return getMovementBounds(stackBounds, true /* adjustForIme */);
+    }
+
+    /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+        final Rect movementBounds = new Rect();
+        getInsetBounds(movementBounds);
+
+        // Apply the movement bounds adjustments based on the current state
+        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+        return movementBounds;
+    }
+
+    /**
      * Applies the minimized offsets to the given stack bounds.
      */
     private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
new file mode 100644
index 0000000..71f88de
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -0,0 +1,122 @@
+/*
+ * 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 android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.UiThread;
+
+import java.util.List;
+
+/**
+ * Controller for the pinned stack container. See {@link StackWindowController}.
+ */
+public class PinnedStackWindowController extends StackWindowController {
+
+    private Rect mTmpBoundsRect = new Rect();
+
+    public PinnedStackWindowController(int stackId, StackWindowListener listener, int displayId,
+            boolean onTop, Rect outBounds) {
+        super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+    }
+
+    /**
+     * Animates the pinned stack.
+     */
+    public void animateResizePinnedStack(Rect bounds, int animationDuration) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Pinned stack container not found :(");
+            }
+
+            // Get non-null fullscreen bounds if the bounds are null
+            final boolean moveToFullscreen = bounds == null;
+            bounds = getPinnedStackAnimationBounds(bounds);
+
+            // If the bounds are truly null, then there was no fullscreen stack at this time, so
+            // animate this to the full display bounds
+            final Rect toBounds;
+            if (bounds == null) {
+                toBounds = new Rect();
+                mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
+            } else {
+                toBounds = bounds;
+            }
+
+            final Rect originalBounds = new Rect();
+            mContainer.getBounds(originalBounds);
+            mContainer.setAnimatingBounds(toBounds);
+            UiThread.getHandler().post(() -> {
+                mService.mBoundsAnimationController.animateBounds(mContainer, originalBounds,
+                        toBounds, animationDuration, moveToFullscreen);
+            });
+        }
+    }
+
+    /**
+     * Sets the current picture-in-picture aspect ratio.
+     */
+    public void setPictureInPictureAspectRatio(float aspectRatio) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+            final Rect targetBounds = new Rect();
+            mContainer.getAnimatingBounds(targetBounds);
+            if (!toBounds.equals(targetBounds)) {
+                animateResizePinnedStack(toBounds, -1 /* duration */);
+            }
+
+            final PinnedStackController pinnedStackController =
+                    mContainer.getDisplayContent().getPinnedStackController();
+            pinnedStackController.setAspectRatio(
+                    pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+                            ? aspectRatio : -1f);
+        }
+    }
+
+    /**
+     * Sets the current picture-in-picture actions.
+     */
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
+        }
+    }
+
+    /**
+     * Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack
+     * animation if necessary.
+     */
+    private Rect getPinnedStackAnimationBounds(Rect bounds) {
+        mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
+        if (bounds == null && !mTmpBoundsRect.isEmpty()) {
+            bounds = new Rect(mTmpBoundsRect);
+        }
+        return bounds;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 142f69a..b0e115b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -199,59 +199,6 @@
         }
     }
 
-    // TODO: This and similar methods should be separated into PinnedStackWindowContainerController
-    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
-        synchronized (mWindowMap) {
-            if (mContainer == null) {
-                throw new IllegalArgumentException("Pinned stack container not found :(");
-            }
-            final Rect originalBounds = new Rect();
-            mContainer.getBounds(originalBounds);
-            mContainer.setAnimatingBounds(bounds);
-            UiThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    mService.mBoundsAnimationController.animateBounds(
-                            mContainer, originalBounds, bounds, animationDuration);
-                }
-            });
-        }
-    }
-
-    /** Sets the current picture-in-picture aspect ratio. */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            final int displayId = mContainer.getDisplayContent().getDisplayId();
-            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
-            final Rect targetBounds = new Rect();
-            mContainer.getAnimatingBounds(targetBounds);
-            if (!toBounds.equals(targetBounds)) {
-                animateResizePinnedStack(toBounds, -1 /* duration */);
-            }
-
-            final PinnedStackController pinnedStackController =
-                    mContainer.getDisplayContent().getPinnedStackController();
-            pinnedStackController.setAspectRatio(
-                    pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
-                            ? aspectRatio : -1f);
-        }
-    }
-
-    /** Sets the current picture-in-picture actions. */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mWindowMap) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
     public void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds,
             Rect outTempInsetBounds, boolean ignoreVisibility) {
         synchronized (mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5f8b694..c7532ac 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -1476,14 +1477,6 @@
         }
     }
 
-    @Override
-    public void getFullScreenBounds(Rect bounds) {
-        // This is currently only used for the pinned stack animation when leaving PiP
-        // (see {@link BoundsAnimationController}), and in that case we need to animate this back
-        // to the full bounds to match the fullscreen stack
-        getDisplayContent().getLogicalDisplayRect(bounds);
-    }
-
     public boolean hasMovementAnimations() {
         return StackId.hasMovementAnimations(mStackId);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9397c9e..474610b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7267,10 +7267,6 @@
         return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
     }
 
-    private DisplayInfo getDefaultDisplayInfoLocked() {
-        return getDefaultDisplayContentLocked().getDisplayInfo();
-    }
-
     public void onDisplayAdded(int displayId) {
         mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
     }