Updating picture-in-picture API.

- Consolidating to enterPictureInPictureMode(), the new method will
  attempt to put the activity into picture-in-picture mode if the
  activity is visible or pausing in a state that would allow us to
  pip it.  Also consolidate the setting of the PiP aspect ratio and
  actions into setPictureInPictureArgs().
- Fixing issue with onPause not completing when moving the
  paused activity between stacks while dispatching onPause

Bug: 33692987
Test: android.server.cts.ActivityManagerPinnedStackTests

Change-Id: I3af2365f31a9b95de4a92eae46b77108947b2a49
diff --git a/api/current.txt b/api/current.txt
index 21ff81e..ec27f7c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3500,8 +3500,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3674,8 +3673,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -5533,6 +5531,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1f590dd..962ab3e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3619,8 +3619,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3795,8 +3794,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -5718,6 +5716,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index f4e71e3..2daf663 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3502,8 +3502,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3676,8 +3675,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -5544,6 +5542,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6771857..556d7ad 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2029,78 +2029,53 @@
     }
 
     /**
-     * Puts the activity in picture-in-picture mode.
+     * Puts the activity in picture-in-picture mode if possible in the current system state. Any
+     * prior calls to {@link #setPictureInPictureArgs(PictureInPictureArgs)} will still apply when
+     * entering picture-in-picture through this call.
+     *
+     * @see #enterPictureInPictureMode(PictureInPictureArgs)
      * @see android.R.attr#supportsPictureInPicture
      */
     public void enterPictureInPictureMode() {
-        try {
-            ActivityManager.getService().enterPictureInPictureMode(mToken);
-        } catch (RemoteException e) {
-        }
+        enterPictureInPictureMode(new PictureInPictureArgs());
     }
 
     /**
-     * Puts the activity in picture-in-picture mode with a given aspect ratio.
+     * Puts the activity in picture-in-picture mode if possible in the current system state with
+     * explicit given arguments. Only the set parameters in {@param args} will override prior calls
+     * {@link #setPictureInPictureArgs(PictureInPictureArgs)}.
+     *
+     * The system may disallow entering picture-in-picture in various cases, including when the
+     * activity is not visible.
+     *
      * @see android.R.attr#supportsPictureInPicture
      *
-     * @param aspectRatio the new aspect ratio of the picture-in-picture.
+     * @param args the explicit non-null arguments to use when entering picture-in-picture.
+     * @return whether the system successfully entered picture-in-picture.
      */
-    public void enterPictureInPictureMode(float aspectRatio) {
+    public boolean enterPictureInPictureMode(@NonNull PictureInPictureArgs args) {
         try {
-            ActivityManagerNative.getDefault().enterPictureInPictureModeWithAspectRatio(mToken,
-                    aspectRatio);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Requests to the system that the activity can be automatically put into picture-in-picture
-     * mode when the user leaves the activity causing it normally to be hidden.  Generally, this
-     * happens when another task is brought to the forground or the task containing this activity
-     * is moved to the background.  This is a *not* a guarantee that the activity will actually be
-     * put in picture-in-picture mode, and depends on a number of factors, including whether there
-     * is already something in picture-in-picture.
-     *
-     * @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
-     *                                        picture-in-picture
-     */
-    public void enterPictureInPictureModeOnMoveToBackground(
-            boolean enterPictureInPictureOnMoveToBg) {
-        try {
-            ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
-                    enterPictureInPictureOnMoveToBg);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Updates the aspect ratio of the current picture-in-picture activity if this activity is
-     * already in picture-in-picture mode, or sets it to be used later if
-     * {@link #enterPictureInPictureModeOnMoveToBackground(boolean)} is requested.
-     *
-     * @param aspectRatio the new aspect ratio of the picture-in-picture.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        try {
-            ActivityManagerNative.getDefault().setPictureInPictureAspectRatio(mToken, aspectRatio);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Updates the set of user actions associated with the picture-in-picture activity.
-     *
-     * @param actions the new actions for picture-in-picture (can be null to reset the set of
-     *                actions).  The maximum number of actions that will be displayed on this device
-     *                is defined by {@link ActivityManager#getMaxNumPictureInPictureActions()}.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        try {
-            if (actions == null) {
-                actions = new ArrayList<>();
+            if (args == null) {
+                throw new IllegalArgumentException("Expected non-null picture-in-picture args");
             }
-            ActivityManagerNative.getDefault().setPictureInPictureActions(mToken,
-                    new ParceledListSlice<RemoteAction>(actions));
+            return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Updates the properties of the picture-in-picture activity, or sets it to be used later when
+     * {@link #enterPictureInPictureMode()} is called.
+     *
+     * @param args the new properties of the picture-in-picture.
+     */
+    public void setPictureInPictureArgs(@NonNull PictureInPictureArgs args) {
+        try {
+            if (args == null) {
+                throw new IllegalArgumentException("Expected non-null picture-in-picture args");
+            }
+            ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 87700dc..4ff843d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -64,36 +64,6 @@
     public static final int APP_TRANSITION_TIMEOUT = 3;
 
     /**
-     * Class to hold deferred properties to apply for picture-in-picture for a given activity.
-     */
-    public static class PictureInPictureArguments {
-        /**
-         * The expected aspect ratio of the picture-in-picture.
-         */
-        public float aspectRatio;
-
-        /**
-         * The set of actions that are associated with this activity when in picture in picture.
-         */
-        public List<RemoteAction> userActions = new ArrayList<>();
-
-        public void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + "aspectRatio=" + aspectRatio);
-            if (userActions.isEmpty()) {
-                pw.println(prefix + "  userActions=[]");
-            } else {
-                pw.println(prefix + "  userActions=[");
-                for (int i = 0; i < userActions.size(); i++) {
-                    RemoteAction action = userActions.get(i);
-                    pw.print(prefix + "    Action[" + i + "]: ");
-                    action.dump("", pw);
-                }
-                pw.println(prefix + "  ]");
-            }
-        }
-    }
-
-    /**
      * Grant Uri permissions from one app to another. This method only extends
      * permission grants if {@code callingUid} has permission to them.
      */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e143255..2c1e7c5 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -34,6 +34,7 @@
 import android.app.IUserSwitchObserver;
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.assist.AssistContent;
@@ -479,12 +480,8 @@
     boolean isInMultiWindowMode(in IBinder token);
     boolean isInPictureInPictureMode(in IBinder token);
     void killPackageDependents(in String packageName, int userId);
-    void enterPictureInPictureMode(in IBinder token);
-    void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio);
-    void enterPictureInPictureModeOnMoveToBackground(in IBinder token,
-            boolean enterPictureInPictureOnMoveToBg);
-    void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
-    void setPictureInPictureActions(in IBinder token, in ParceledListSlice actions);
+    boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureArgs args);
+    void setPictureInPictureArgs(in IBinder token, in PictureInPictureArgs args);
     void activityRelaunched(in IBinder token);
     IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
     /**
diff --git a/core/java/android/app/PictureInPictureArgs.aidl b/core/java/android/app/PictureInPictureArgs.aidl
new file mode 100644
index 0000000..49df39a
--- /dev/null
+++ b/core/java/android/app/PictureInPictureArgs.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.app;
+
+parcelable PictureInPictureArgs;
diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java
new file mode 100644
index 0000000..fbdcbf4
--- /dev/null
+++ b/core/java/android/app/PictureInPictureArgs.java
@@ -0,0 +1,175 @@
+/*
+ * 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 android.app;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a set of arguments used to initialize the picture-in-picture mode.
+ */
+public final class PictureInPictureArgs implements Parcelable {
+
+    /**
+     * The expected aspect ratio of the picture-in-picture.
+     */
+    @Nullable
+    private Float mAspectRatio;
+
+    /**
+     * The set of actions that are associated with this activity when in picture in picture.
+     */
+    @Nullable
+    private List<RemoteAction> mUserActions;
+
+    PictureInPictureArgs(Parcel in) {
+        if (in.readInt() != 0) {
+            mAspectRatio = in.readFloat();
+        }
+        if (in.readInt() != 0) {
+            mUserActions = new ArrayList<>();
+            in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
+        }
+    }
+
+    /**
+     * Creates a new set of picture-in-picture arguments.
+     */
+    public PictureInPictureArgs() {
+        // Empty constructor
+    }
+
+    /**
+     * Creates a new set of picture-in-picture arguments from the given {@param aspectRatio} and
+     * {@param actions}.
+     */
+    public PictureInPictureArgs(float aspectRatio, List<RemoteAction> actions) {
+        mAspectRatio = aspectRatio;
+        if (actions != null) {
+            mUserActions = new ArrayList<>(actions);
+        }
+    }
+
+    /**
+     * Copies the set parameters from the other picture-in-picture args.
+     * @hide
+     */
+    public void copyOnlySet(PictureInPictureArgs otherArgs) {
+        if (otherArgs.hasSetAspectRatio()) {
+            mAspectRatio = otherArgs.mAspectRatio;
+        }
+        if (otherArgs.hasSetActions()) {
+            mUserActions = otherArgs.mUserActions;
+        }
+    }
+
+    /**
+     * Sets the aspect ratio.
+     * @param aspectRatio the new aspect ratio for picture-in-picture.
+     */
+    public void setAspectRatio(float aspectRatio) {
+        mAspectRatio = aspectRatio;
+    }
+
+    /**
+     * @return the aspect ratio. If none is set, return 0.
+     * @hide
+     */
+    public float getAspectRatio() {
+        if (mAspectRatio != null) {
+            return mAspectRatio;
+        }
+        return 0f;
+    }
+
+    /**
+     * @return whether the aspect ratio is set.
+     * @hide
+     */
+    public boolean hasSetAspectRatio() {
+        return mAspectRatio != null;
+    }
+
+    /**
+     * Sets the user actions.
+     * @param actions the new actions to show in the picture-in-picture menu.
+     */
+    public void setActions(List<RemoteAction> actions) {
+        if (mUserActions != null) {
+            mUserActions = null;
+        }
+        if (actions != null) {
+            mUserActions = new ArrayList<>(actions);
+        }
+    }
+
+    /**
+     * @return the set of user actions.
+     * @hide
+     */
+    public List<RemoteAction> getActions() {
+        return mUserActions;
+    }
+
+    /**
+     * @return whether the user actions are set.
+     * @hide
+     */
+    public boolean hasSetActions() {
+        return mUserActions != null;
+    }
+
+    @Override
+    public PictureInPictureArgs clone() {
+        return new PictureInPictureArgs(mAspectRatio, mUserActions);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        if (mAspectRatio != null) {
+            out.writeInt(1);
+            out.writeFloat(mAspectRatio);
+        } else {
+            out.writeInt(0);
+        }
+        if (mUserActions != null) {
+            out.writeInt(1);
+            out.writeParcelableList(mUserActions, 0);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public static final Creator<PictureInPictureArgs> CREATOR =
+            new Creator<PictureInPictureArgs>() {
+                public PictureInPictureArgs createFromParcel(Parcel in) {
+                    return new PictureInPictureArgs(in);
+                }
+                public PictureInPictureArgs[] newArray(int size) {
+                    return new PictureInPictureArgs[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a324eb..db7d505 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -151,7 +151,6 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -182,6 +181,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ProfilerInfo;
 import android.app.RemoteAction;
 import android.app.WaitResult;
@@ -229,7 +229,6 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.location.LocationManager;
@@ -7556,45 +7555,31 @@
     }
 
     @Override
-    public void enterPictureInPictureMode(IBinder token) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, -1f /* aspectRatio */,
-                false /* checkAspectRatio */);
-    }
-
-    @Override
-    public void enterPictureInPictureModeWithAspectRatio(IBinder token, float aspectRatio) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio, true /* checkAspectRatio */);
-    }
-
-    @Override
-    public void enterPictureInPictureModeOnMoveToBackground(IBinder token,
-            boolean enterPictureInPictureOnMoveToBg) {
+    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureArgs args) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "enterPictureInPictureModeOnMoveToBackground", token, -1f /* aspectRatio */,
-                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+                final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
+                        "enterPictureInPictureMode", token, args);
 
-                r.supportsPipOnMoveToBackground = enterPictureInPictureOnMoveToBg;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
+                // Activity supports picture-in-picture, now check that we can enter PiP at this
+                // point, if it is
+                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode")) {
+                    return false;
+                }
 
-    private void enterPictureInPictureMode(IBinder token, int displayId, float aspectRatio,
-            boolean checkAspectRatio) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
-                        true /* checkActivityVisibility */);
                 final Runnable enterPipRunnable = () -> {
-                    r.pictureInPictureArgs.aspectRatio = aspectRatio;
-                    enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
-                            true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+                    // Only update the saved args from the args that are set
+                    r.pictureInPictureArgs.copyOnlySet(args);
+                    final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                    final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                    final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
+                            ? mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
+                                    aspectRatio)
+                            : mWindowManager.getPictureInPictureDefaultBounds(DEFAULT_DISPLAY);
+                    mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
+                            bounds, true /* moveHomeStackToFront */);
+                    mWindowManager.setPictureInPictureActions(actions);
                 };
 
                 if (isKeyguardLocked()) {
@@ -7625,35 +7610,7 @@
                     // Enter picture in picture immediately otherwise
                     enterPipRunnable.run();
                 }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    void enterPictureInPictureModeLocked(ActivityRecord r, int displayId,
-            PictureInPictureArguments pipArgs, boolean moveHomeStackToFront, String reason) {
-        final Rect bounds = isValidPictureInPictureAspectRatio(pipArgs.aspectRatio)
-                ? mWindowManager.getPictureInPictureBounds(displayId, pipArgs.aspectRatio)
-                : mWindowManager.getPictureInPictureDefaultBounds(displayId);
-        mStackSupervisor.moveActivityToPinnedStackLocked(r, reason, bounds, moveHomeStackToFront);
-        mWindowManager.setPictureInPictureActions(pipArgs.userActions);
-    }
-
-    @Override
-    public void setPictureInPictureAspectRatio(IBinder token, float aspectRatio) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "setPictureInPictureAspectRatio", token, aspectRatio,
-                        true /* checkAspectRatio */, false /* checkActivityVisibility */);
-
-                r.pictureInPictureArgs.aspectRatio = aspectRatio;
-                if (r.getStack().getStackId() == PINNED_STACK_ID) {
-                    // If the activity is already in picture-in-picture, update the pinned stack now
-                    mWindowManager.setPictureInPictureAspectRatio(aspectRatio);
-                }
+                return true;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -7661,26 +7618,21 @@
     }
 
     @Override
-    public void setPictureInPictureActions(IBinder token, ParceledListSlice actionsList) {
+    public void setPictureInPictureArgs(IBinder token, final PictureInPictureArgs args) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "setPictureInPictureActions", token, -1 /* aspectRatio */,
-                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+                final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
+                        "setPictureInPictureArgs", token, args);
 
-                final List<RemoteAction> actions = actionsList.getList();
-                if (actions.size() > ActivityManager.getMaxNumPictureInPictureActions()) {
-                    throw new IllegalArgumentException("setPictureInPictureActions: Invalid number"
-                            + " of picture-in-picture actions.  Only a maximum of "
-                            + ActivityManager.getMaxNumPictureInPictureActions()
-                            + " actions allowed");
-                }
-
-                r.pictureInPictureArgs.userActions = actions;
+                // Only update the saved args from the args that are set
+                r.pictureInPictureArgs.copyOnlySet(args);
                 if (r.getStack().getStackId() == PINNED_STACK_ID) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
-                    mWindowManager.setPictureInPictureActions(actions);
+                    mWindowManager.setPictureInPictureAspectRatio(
+                            r.pictureInPictureArgs.getAspectRatio());
+                    mWindowManager.setPictureInPictureActions(
+                            r.pictureInPictureArgs.getActions());
                 }
             }
         } finally {
@@ -7696,14 +7648,10 @@
      * Checks the state of the system and the activity associated with the given {@param token} to
      * verify that picture-in-picture is supported for that activity.
      *
-     * @param checkAspectRatio whether or not to check {@param aspectRatio} is within a valid range
-     * @param checkActivityVisibility whether or not to enforce that the activity is currently
-     *                                visible
-     *
      * @return the activity record for the given {@param token} if all the checks pass.
      */
-    private ActivityRecord ensureValidPictureInPictureActivityLocked(String caller, IBinder token,
-            float aspectRatio, boolean checkAspectRatio, boolean checkActivityVisibility) {
+    private ActivityRecord ensureValidPictureInPictureActivityArgsLocked(String caller,
+            IBinder token, PictureInPictureArgs args) {
         if (!mSupportsPictureInPicture) {
             throw new IllegalStateException(caller
                     + ": Device doesn't support picture-in-picture mode.");
@@ -7715,10 +7663,9 @@
                     + ": Can't find activity for token=" + token);
         }
 
-        if (!r.canEnterPictureInPicture(checkActivityVisibility)) {
-            throw new IllegalArgumentException(caller
-                    + ": Current activity does not support picture-in-picture or is not "
-                    + "visible r=" + r);
+        if (!r.supportsPictureInPicture()) {
+            throw new IllegalStateException(caller
+                    + ": Current activity does not support picture-in-picture.");
         }
 
         if (r.getStack().isHomeStack()) {
@@ -7726,12 +7673,20 @@
                     + ": Activities on the home stack not supported");
         }
 
-        if (checkAspectRatio && !isValidPictureInPictureAspectRatio(aspectRatio)) {
+        if (args.hasSetAspectRatio()
+                && !isValidPictureInPictureAspectRatio(args.getAspectRatio())) {
             throw new IllegalArgumentException(String.format(caller
                     + ": Aspect ratio is too extreme (must be between %f and %f).",
                             mMinPipAspectRatio, mMaxPipAspectRatio));
         }
 
+        if (args.hasSetActions()
+                && args.getActions().size() > ActivityManager.getMaxNumPictureInPictureActions()) {
+            throw new IllegalArgumentException(String.format(caller + ": Invalid number of"
+                    + "picture-in-picture actions.  Only a maximum of %d actions allowed",
+                            ActivityManager.getMaxNumPictureInPictureActions()));
+        }
+
         return r;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a2fb9f9..6134d75 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -63,9 +63,9 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -229,13 +229,10 @@
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
-    boolean supportsPipOnMoveToBackground;   // Supports automatically entering picture-in-picture
-        // when this activity is hidden. This flag is requested by the activity.
-    private boolean enterPipOnMoveToBackground; // Flag to enter picture in picture when this
-        // activity is made invisible. This flag is set specifically when another task is being
-        // launched or moved to the front which may cause this activity to try and enter PiP
-        // when it is next made invisible.
-    PictureInPictureArguments pictureInPictureArgs = new PictureInPictureArguments();  // The PiP
+    boolean supportsPictureInPictureWhilePausing;  // This flag is set by the system to indicate
+        // that the activity can enter picture in picture while pausing (ie. only when another
+        // task is brought to front or started)
+    PictureInPictureArgs pictureInPictureArgs = new PictureInPictureArgs();  // The PiP
         // arguments used when deferring the entering of picture-in-picture.
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
@@ -453,12 +450,8 @@
         if (info != null) {
             pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
         }
-        if (supportsPipOnMoveToBackground) {
-            pw.println(prefix + "supportsPipOnMoveToBackground=1");
-            pw.println(prefix + "enterPipOnMoveToBackground=" +
-                    (enterPipOnMoveToBackground ? 1 : 0));
-            pictureInPictureArgs.dump(pw, prefix);
-        }
+        pw.println(prefix + "supportsPictureInPictureWhilePausing: "
+                + supportsPictureInPictureWhilePausing);
     }
 
     private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
@@ -819,23 +812,6 @@
     }
 
     /**
-     * If this activity has requested that it auto-enter picture-in-picture and we can actually do
-     * this, then mark it to enter picture in picture at that point.
-     */
-    void setEnterPipOnMoveToBackground(boolean enterPipOnInvisible) {
-        if (supportsPipOnMoveToBackground) {
-            enterPipOnMoveToBackground = enterPipOnInvisible;
-        }
-    }
-
-    /**
-     * @return whether to enter PiP when this activity is made invisible.
-     */
-    public boolean shouldEnterPictureInPictureOnInvisible() {
-        return enterPipOnMoveToBackground;
-    }
-
-    /**
      * @return Stack value from current task, null if there is no task.
      */
     ActivityStack getStack() {
@@ -918,24 +894,34 @@
     }
 
     /**
-     * @return whether this activity is currently allowed to enter PIP, if
-     * {@param checkActivityVisibility} is set, then the current activity visibility is taken into
-     * account.
+     * @return whether this activity is currently allowed to enter PIP, throwing an exception if
+     *         the activity is not currently visible.
      */
-    boolean canEnterPictureInPicture(boolean checkActivityVisibility) {
-        if (!checkActivityVisibility) {
-            return supportsPictureInPicture();
+    boolean checkEnterPictureInPictureState(String caller) {
+        boolean isKeyguardLocked = service.isKeyguardLocked();
+        boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+        switch (state) {
+            case RESUMED:
+                // When visible, allow entering PiP if not on the lockscreen.  If there is another
+                // PiP activity, the logic to handle that comes later in enterPictureInPictureMode()
+                return !isKeyguardLocked;
+            case PAUSING:
+            case PAUSED:
+                // When pausing, only allow enter PiP if not on the lockscreen and there is not
+                // already an existing PiP activity
+                return !isKeyguardLocked && !hasPinnedStack && supportsPictureInPictureWhilePausing;
+            case STOPPING:
+                // When stopping in a valid state, then only allow enter PiP as in the pause state.
+                // Otherwise, fall through to throw an exception if the caller is trying to enter
+                // PiP in an invalid stopping state.
+                if (supportsPictureInPictureWhilePausing) {
+                    return !isKeyguardLocked && !hasPinnedStack;
+                }
+            default:
+                throw new IllegalStateException(caller
+                        + ": Current activity is not visible (state=" + state.name() + ") "
+                        + "r=" + this);
         }
-
-        if (supportsPictureInPicture()) {
-            switch (state) {
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                    return true;
-            }
-        }
-        return false;
     }
 
     boolean canGoInDockedStack() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4df0cb1..cd11d21 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1698,9 +1698,7 @@
                                 + stackInvisible + " behindFullscreenActivity="
                                 + behindFullscreenActivity + " mLaunchTaskBehind="
                                 + r.mLaunchTaskBehind);
-                        if (!enterPictureInPictureOnActivityInvisible(r)) {
-                            makeInvisible(r, visibleBehind);
-                        }
+                        makeInvisible(r, visibleBehind);
                     }
                 }
                 if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
@@ -1859,35 +1857,6 @@
         return false;
     }
 
-    /**
-     * Attempts to enter picture-in-picture if the activity that is being made invisible supports
-     * it.  If not, then
-     *
-     * @return whether or not picture-in-picture mode was entered.
-     */
-    private boolean enterPictureInPictureOnActivityInvisible(ActivityRecord r) {
-        final boolean hasPinnedStack =
-                mStackSupervisor.getStack(PINNED_STACK_ID) != null;
-        final boolean isKeyguardLocked = mService.isKeyguardLocked();
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, " enterPictureInPictureOnInvisible="
-                + r.shouldEnterPictureInPictureOnInvisible()
-                + " hasPinnedStack=" + hasPinnedStack
-                + " isKeyguardLocked=" + isKeyguardLocked);
-        if (!hasPinnedStack && !isKeyguardLocked && r.visible &&
-                r.shouldEnterPictureInPictureOnInvisible()) {
-            r.setEnterPipOnMoveToBackground(false);
-
-            // Enter picture in picture, but don't move the home stack to the front
-            // since it will affect the focused stack's visibility and occlude
-            // starting activities
-            mService.enterPictureInPictureModeLocked(r, r.getDisplayId(),
-                    r.pictureInPictureArgs, false /* moveHomeStackToFront */,
-                    "ensureActivitiesVisibleLocked");
-            return true;
-        }
-        return false;
-    }
-
     private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
         if (!r.visible) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
@@ -1906,6 +1875,10 @@
                                 "Scheduling invisibility: " + r);
                         r.app.thread.scheduleWindowVisibility(r.appToken, false);
                     }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    r.supportsPictureInPictureWhilePausing = false;
                     break;
 
                 case INITIALIZING:
@@ -2187,15 +2160,16 @@
 
         mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
 
-        // We need to start pausing the current activity so the top one can be resumed...
-        final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
-        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause);
+        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
+        // to be paused, while at the same time resuming the new resume activity
+        final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
+        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
         if (mResumedActivity != null) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Pausing " + mResumedActivity);
-            pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);
+            pausing |= startPausingLocked(userLeaving, false, next, false);
         }
-        if (pausing) {
+        if (pausing && !resumeWhilePausing) {
             if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
                     "resumeTopActivityLocked: Skip resume: need to start pausing");
             // At this point we want to put the upcoming activity's process
@@ -2696,10 +2670,10 @@
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
                     } else {
-                        // If a new task is being launched, then mark the existing top activity to
-                        // enter picture-in-picture if it supports auto-entering PiP
+                        // If a new task is being launched, then mark the existing top activity as
+                        // supporting picture-in-picture while pausing
                         if (focusedTopActivity != null) {
-                            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+                            focusedTopActivity.supportsPictureInPictureWhilePausing = true;
                         }
                         transit = TRANSIT_TASK_OPEN;
                     }
@@ -4245,10 +4219,10 @@
         } else {
             updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
         }
-        // If a new task is moved to the front, then mark the existing top activity to enter
-        // picture-in-picture if it supports auto-entering PiP
+        // If a new task is moved to the front, then mark the existing top activity as supporting
+        // picture-in-picture while paused
         if (focusedTopActivity != null) {
-            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+            focusedTopActivity.supportsPictureInPictureWhilePausing = true;
         }
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -4966,8 +4940,8 @@
         }
     }
 
-    void moveToFrontAndResumeStateIfNeeded(
-            ActivityRecord r, boolean moveToFront, boolean setResume, String reason) {
+    void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
+            boolean setPause, String reason) {
         if (!moveToFront) {
             return;
         }
@@ -4978,6 +4952,10 @@
         if (setResume) {
             mResumedActivity = r;
         }
+        // If the activity was previously pausing, then ensure we transfer that as well
+        if (setPause) {
+            mPausingActivity = r;
+        }
         // Move the stack in which we are placing the activity to the front. The call will also
         // make sure the activity focus is set.
         moveToFront(reason);
@@ -4998,6 +4976,7 @@
         final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
                 && (mStackSupervisor.topRunningActivityLocked() == r);
         final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+        final boolean wasPaused = prevStack.mPausingActivity == r;
 
         final TaskRecord task = createTaskRecord(
                 mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
@@ -5005,10 +4984,14 @@
         r.setTask(task, null);
         task.addActivityToTop(r);
         mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
-        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, wasPaused,
+                "moveActivityToStack");
         if (wasResumed) {
             prevStack.mResumedActivity = null;
         }
+        if (wasPaused) {
+            prevStack.mPausingActivity = null;
+        }
     }
 
     public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4fe8939..1224abd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2617,6 +2617,7 @@
         final ActivityStack prevStack = task.getStack();
         final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
         final boolean wasResumed = prevStack.mResumedActivity == r;
+        final boolean wasPaused = prevStack.mPausingActivity == r;
         // In some cases the focused stack isn't the front stack. E.g. pinned stack.
         // Whenever we are moving the top activity from the front stack we want to make sure to move
         // the stack to the front.
@@ -2641,10 +2642,19 @@
         task.mTemporarilyUnresizable = false;
         task.reparent(stack.mStackId, toTop ? MAX_VALUE : 0, reason);
 
+        // Reset the resumed activity on the previous stack
+        if (wasResumed) {
+            prevStack.mResumedActivity = null;
+        }
+        // Reset the paused activity on the previous stack
+        if (wasPaused) {
+            prevStack.mPausingActivity = null;
+        }
+
         // If the task had focus before (or we're requested to move focus),
         // move focus to the new stack by moving the stack to the front.
-        stack.moveToFrontAndResumeStateIfNeeded(
-                r, forceFocus || wasFocused || wasFront, wasResumed, reason);
+        stack.moveToFrontAndResumeStateIfNeeded(r, forceFocus || wasFocused || wasFront, wasResumed,
+                wasPaused, reason);
 
         return stack;
     }
@@ -2795,8 +2805,12 @@
 
             if (task.mActivities.size() == 1) {
                 // There is only one activity in the task. So, we can just move the task over to
-                // the stack without re-parenting the activity in a different task.
-                if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+                // the stack without re-parenting the activity in a different task.  We don't
+                // move the home stack forward if we are currently entering picture-in-picture
+                // while pausing because that changes the focused stack and may prevent the new
+                // starting activity from resuming.
+                if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE
+                        && !r.supportsPictureInPictureWhilePausing) {
                     // Move the home stack forward if the task we just moved to the pinned stack
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
@@ -2808,6 +2822,10 @@
                 // reveal/leave the other activities in their original task
                 stack.moveActivityToStack(r);
             }
+
+            // Reset the state that indicates it can enter PiP while pausing after we've moved it
+            // to the pinned stack
+            r.supportsPictureInPictureWhilePausing = false;
         } finally {
             mWindowManager.continueSurfaceLayout();
         }
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index bfb4269..08f9b45 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -333,7 +333,9 @@
      */
     void setActions(List<RemoteAction> actions) {
         mActions.clear();
-        mActions.addAll(actions);
+        if (actions != null) {
+            mActions.addAll(actions);
+        }
         notifyActionsChanged(mActions);
     }