APIs for activity to know when its windowing/pip modes change

Added APIs that allow activities to ask the system if they are currently
in multi-window or picture-in-picture mode and also get notified when
their modes change.

Bug: 25509834
Bug: 25683717
Change-Id: I4b8c316a49940bd6a8b31a93b345f9fd725a4721
diff --git a/api/current.txt b/api/current.txt
index c017754..feb43da 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3411,6 +3411,8 @@
     method public android.view.Window getWindow();
     method public android.view.WindowManager getWindowManager();
     method public boolean hasWindowFocus();
+    method public boolean inMultiWindowMode();
+    method public boolean inPictureInPictureMode();
     method public void invalidateOptionsMenu();
     method public boolean isChangingConfigurations();
     method public final boolean isChild();
@@ -3461,6 +3463,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
     method protected void onNewIntent(android.content.Intent);
@@ -3468,6 +3471,7 @@
     method public void onOptionsMenuClosed(android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method protected void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
     method protected void onPostCreate(android.os.Bundle);
     method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
     method protected void onPostResume();
diff --git a/api/system-current.txt b/api/system-current.txt
index 8128819..bc7983a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3514,6 +3514,8 @@
     method public android.view.Window getWindow();
     method public android.view.WindowManager getWindowManager();
     method public boolean hasWindowFocus();
+    method public boolean inMultiWindowMode();
+    method public boolean inPictureInPictureMode();
     method public void invalidateOptionsMenu();
     method public boolean isBackgroundVisibleBehind();
     method public boolean isChangingConfigurations();
@@ -3566,6 +3568,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
     method protected void onNewIntent(android.content.Intent);
@@ -3573,6 +3576,7 @@
     method public void onOptionsMenuClosed(android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method protected void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
     method protected void onPostCreate(android.os.Bundle);
     method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
     method protected void onPostResume();
diff --git a/api/test-current.txt b/api/test-current.txt
index d05dba2..3bcfd67 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3411,6 +3411,8 @@
     method public android.view.Window getWindow();
     method public android.view.WindowManager getWindowManager();
     method public boolean hasWindowFocus();
+    method public boolean inMultiWindowMode();
+    method public boolean inPictureInPictureMode();
     method public void invalidateOptionsMenu();
     method public boolean isChangingConfigurations();
     method public final boolean isChild();
@@ -3461,6 +3463,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
     method protected void onNewIntent(android.content.Intent);
@@ -3468,6 +3471,7 @@
     method public void onOptionsMenuClosed(android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method protected void onPause();
+    method public void onPictureInPictureModeChanged(boolean);
     method protected void onPostCreate(android.os.Bundle);
     method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
     method protected void onPostResume();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8bb0ff5..93c6bef 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1727,6 +1727,57 @@
     }
 
     /**
+     * Called by the system when the activity changes from fullscreen mode to multi-window mode and
+     * visa-versa.
+     * @see android.R.attr#resizeableActivity
+     *
+     * @param multiWindowMode True if the activity is in multi-window mode.
+     */
+    public void onMultiWindowModeChanged(boolean multiWindowMode) {
+        if (DEBUG_LIFECYCLE) Slog.v(TAG,
+                "onMultiWindowModeChanged " + this + ": " + multiWindowMode);
+    }
+
+    /**
+     * Returns true if the activity is currently in multi-window mode.
+     * @see android.R.attr#resizeableActivity
+     *
+     * @return True if the activity is in multi-window mode.
+     */
+    public boolean inMultiWindowMode() {
+        try {
+            return ActivityManagerNative.getDefault().inMultiWindowMode(mToken);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Called by the system when the activity changes to and from picture-in-picture mode.
+     * @see android.R.attr#supportsPictureInPicture
+     *
+     * @param pictureInPictureMode True if the activity is in picture-in-picture mode.
+     */
+    public void onPictureInPictureModeChanged(boolean pictureInPictureMode) {
+        if (DEBUG_LIFECYCLE) Slog.v(TAG,
+                "onPictureInPictureModeChanged " + this + ": " + pictureInPictureMode);
+    }
+
+    /**
+     * Returns true if the activity is currently in picture-in-picture mode.
+     * @see android.R.attr#supportsPictureInPicture
+     *
+     * @return True if the activity is in picture-in-picture mode.
+     */
+    public boolean inPictureInPictureMode() {
+        try {
+            return ActivityManagerNative.getDefault().inPictureInPictureMode(mToken);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * Called by the system when the device configuration changes while your
      * activity is running.  Note that this will <em>only</em> be called if
      * you have selected configurations you would like to handle with the
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c05d5e8..ee2efcc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2733,6 +2733,22 @@
             reply.writeInt(res);
             return true;
         }
+        case IN_MULTI_WINDOW_MODE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final IBinder token = data.readStrongBinder();
+            final boolean multiWindowMode = inMultiWindowMode(token);
+            reply.writeNoException();
+            reply.writeInt(multiWindowMode ? 1 : 0);
+            return true;
+        }
+        case IN_PICTURE_IN_PICTURE_MODE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final IBinder token = data.readStrongBinder();
+            final boolean pipMode = inPictureInPictureMode(token);
+            reply.writeNoException();
+            reply.writeInt(pipMode ? 1 : 0);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -6366,5 +6382,33 @@
         return res;
     }
 
+    @Override
+    public boolean inMultiWindowMode(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IN_MULTI_WINDOW_MODE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        final boolean multiWindowMode = reply.readInt() == 1 ? true : false;
+        data.recycle();
+        reply.recycle();
+        return multiWindowMode;
+    }
+
+    @Override
+    public boolean inPictureInPictureMode(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IN_PICTURE_IN_PICTURE_MODE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        final boolean pipMode = reply.readInt() == 1 ? true : false;
+        data.recycle();
+        reply.recycle();
+        return pipMode;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e64d13a..f3539ff 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1275,6 +1275,18 @@
             } catch (IOException e) {
             }
         }
+
+        @Override
+        public void scheduleMultiWindowModeChanged(IBinder token, boolean multiWindowMode)
+                throws RemoteException {
+            sendMessage(H.MULTI_WINDOW_MODE_CHANGED, token, multiWindowMode ? 1 : 0);
+        }
+
+        @Override
+        public void schedulePictureInPictureModeChanged(IBinder token, boolean pipMode)
+                throws RemoteException {
+            sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, token, pipMode ? 1 : 0);
+        }
     }
 
     private int getLifecycleSeq() {
@@ -1336,6 +1348,8 @@
         public static final int ENTER_ANIMATION_COMPLETE = 149;
         public static final int START_BINDER_TRACKING = 150;
         public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
+        public static final int MULTI_WINDOW_MODE_CHANGED = 152;
+        public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1389,6 +1403,8 @@
                     case CANCEL_VISIBLE_BEHIND: return "CANCEL_VISIBLE_BEHIND";
                     case BACKGROUND_VISIBLE_BEHIND_CHANGED: return "BACKGROUND_VISIBLE_BEHIND_CHANGED";
                     case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
+                    case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
+                    case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
                 }
             }
             return Integer.toString(code);
@@ -1632,6 +1648,12 @@
                 case STOP_BINDER_TRACKING_AND_DUMP:
                     handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
                     break;
+                case MULTI_WINDOW_MODE_CHANGED:
+                    handleMultiWindowModeChanged((IBinder) msg.obj, msg.arg1 == 1);
+                    break;
+                case PICTURE_IN_PICTURE_MODE_CHANGED:
+                    handlePictureInPictureModeChanged((IBinder) msg.obj, msg.arg1 == 1);
+                    break;
             }
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
@@ -2814,6 +2836,20 @@
         }
     }
 
+    private void handleMultiWindowModeChanged(IBinder token, boolean multiWindowMode) {
+        final ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onMultiWindowModeChanged(multiWindowMode);
+        }
+    }
+
+    private void handlePictureInPictureModeChanged(IBinder token, boolean pipMode) {
+        final ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onPictureInPictureModeChanged(pipMode);
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 44387de..5951c8d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -720,6 +720,24 @@
             return true;
         }
 
+        case SCHEDULE_MULTI_WINDOW_MODE_CHANGED_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            final IBinder b = data.readStrongBinder();
+            final boolean multiWindowMode = data.readInt() != 0;
+            scheduleMultiWindowModeChanged(b, multiWindowMode);
+            return true;
+        }
+
+        case SCHEDULE_PICTURE_IN_PICTURE_MODE_CHANGED_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            final IBinder b = data.readStrongBinder();
+            final boolean pipMode = data.readInt() != 0;
+            schedulePictureInPictureModeChanged(b, pipMode);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -1454,4 +1472,28 @@
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public final void scheduleMultiWindowModeChanged(
+            IBinder token, boolean multiWindowMode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(multiWindowMode ? 1 : 0);
+        mRemote.transact(SCHEDULE_MULTI_WINDOW_MODE_CHANGED_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    @Override
+    public final void schedulePictureInPictureModeChanged(IBinder token, boolean pipMode)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(pipMode ? 1 : 0);
+        mRemote.transact(SCHEDULE_PICTURE_IN_PICTURE_MODE_CHANGED_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 38c7957..9a01b7b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -545,6 +545,10 @@
 
     public int getAppStartMode(int uid, String packageName) throws RemoteException;
 
+    public boolean inMultiWindowMode(IBinder token) throws RemoteException;
+
+    public boolean inPictureInPictureMode(IBinder token) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -906,4 +910,6 @@
     int MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 349;
     int GET_APP_START_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 350;
     int UNLOCK_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 351;
+    int IN_MULTI_WINDOW_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 352;
+    int IN_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 353;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 64045f334..dc67026 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -152,6 +152,8 @@
     void notifyCleartextNetwork(byte[] firstPacket) throws RemoteException;
     void startBinderTracking() throws RemoteException;
     void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException;
+    void scheduleMultiWindowModeChanged(IBinder token, boolean multiWindowMode) throws RemoteException;
+    void schedulePictureInPictureModeChanged(IBinder token, boolean multiWindowMode) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -212,4 +214,6 @@
     int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55;
     int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56;
     int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57;
+    int SCHEDULE_MULTI_WINDOW_MODE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
+    int SCHEDULE_PICTURE_IN_PICTURE_MODE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b769d39..7d768bd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,65 +16,9 @@
 
 package com.android.server.am;
 
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
-import android.Manifest;
-import android.app.AppOpsManager;
-import android.app.ApplicationThreadNative;
-import android.app.BroadcastOptions;
-import android.app.IActivityContainer;
-import android.app.IActivityContainerCallback;
-import android.app.IAppTask;
-import android.app.ITaskStackListener;
-import android.app.ProfilerInfo;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
-import android.appwidget.AppWidgetManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PermissionInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.os.Trace;
-import android.os.TransactionTooLargeException;
-import android.os.WorkSource;
-import android.os.storage.IMountService;
-import android.os.storage.MountServiceInternal;
-import android.os.storage.StorageManager;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionSession;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.SparseIntArray;
-import android.view.Display;
-
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.AssistUtils;
@@ -185,6 +129,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PathPermission;
 import android.content.pm.PermissionInfo;
@@ -305,6 +250,7 @@
 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.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
@@ -333,6 +279,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -499,9 +446,6 @@
     // interaction with foreground processes.
     static final long USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
 
-    // Maximum number of users we allow to be running at a time.
-    static final int MAX_RUNNING_USERS = 3;
-
     // This is the amount of time we allow an app to settle after it goes into the background,
     // before we start restricting what it can do.
     static final int BACKGROUND_SETTLE_TIME = 1*60*1000;
@@ -7210,6 +7154,29 @@
         }
     }
 
+    @Override
+    public boolean inMultiWindowMode(IBinder token) {
+        synchronized(this) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+            // An activity is consider to be in multi-window mode if its task isn't fullscreen.
+            return !r.task.mFullscreen;
+        }
+    }
+
+    @Override
+    public boolean inPictureInPictureMode(IBinder token) {
+        synchronized(this) {
+            final ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack == null) {
+                return false;
+            }
+            return stack.mStackId == PINNED_STACK_ID;
+        }
+    }
+
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -8863,7 +8830,7 @@
 
                 task.inRecents = true;
                 mRecentTasks.add(task);
-                r.task.stack.addTask(task, false, false);
+                r.task.stack.addTask(task, false, "addAppTask");
 
                 task.setLastThumbnailLocked(thumbnail);
                 task.freeLastThumbnail();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 65497cf..bad71b2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4652,7 +4652,7 @@
         TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
                 voiceInteractor);
         // add the task to stack first, mTaskPositioner might need the stack association
-        addTask(task, toTop, false);
+        addTask(task, toTop, "createTaskRecord");
         final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
         if (!layoutTaskInStack(task, info.layout) && mBounds != null && task.mResizeable
                 && !isLockscreenShown) {
@@ -4673,7 +4673,9 @@
         return new ArrayList<>(mTaskHistory);
     }
 
-    void addTask(final TaskRecord task, final boolean toTop, boolean moving) {
+    void addTask(final TaskRecord task, final boolean toTop, String reason) {
+        final ActivityStack prevStack = preAddTask(task, reason);
+
         task.stack = this;
         if (toTop) {
             insertTaskAtTop(task, null);
@@ -4681,18 +4683,31 @@
             mTaskHistory.add(0, task);
             updateTaskMovement(task, false);
         }
-        if (!moving && task.voiceSession != null) {
-            try {
-                task.voiceSession.taskStarted(task.intent, task.taskId);
-            } catch (RemoteException e) {
-            }
-        }
+        postAddTask(task, prevStack);
     }
 
-    void positionTask(final TaskRecord task, int position, boolean moving) {
+    void positionTask(final TaskRecord task, int position) {
+        final ActivityStack prevStack = preAddTask(task, "positionTask");
         task.stack = this;
         insertTaskAtPosition(task, position);
-        if (!moving && task.voiceSession != null) {
+        postAddTask(task, prevStack);
+    }
+
+    private ActivityStack preAddTask(TaskRecord task, String reason) {
+        final ActivityStack prevStack = task.stack;
+        if (prevStack != null && prevStack != this) {
+            prevStack.removeTask(task, reason, MOVING);
+        }
+        return prevStack;
+    }
+
+    private void postAddTask(TaskRecord task, ActivityStack prevStack) {
+        if (prevStack != null) {
+            if (prevStack != this
+                    && (prevStack.mStackId == PINNED_STACK_ID || mStackId == PINNED_STACK_ID)) {
+                task.reportPictureInPictureModeChange();
+            }
+        } else if (task.voiceSession != null) {
             try {
                 task.voiceSession.taskStarted(task.intent, task.taskId);
             } catch (RemoteException e) {
@@ -4754,6 +4769,7 @@
         r.setTask(task, null);
         task.addActivityToTop(r);
         setAppTask(r, task);
+        task.reportPictureInPictureModeChange();
         setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 18b3e62..f4ba19f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2337,8 +2337,8 @@
                                 // above. Go ahead and reset it.
                                 targetStack = computeStackFocus(
                                         sourceRecord, false /* newTask */, null /* bounds */);
-                                targetStack.addTask(
-                                        task, !launchTaskBehind /* toTop */, false /* moving */);
+                                targetStack.addTask(task,
+                                        !launchTaskBehind /* toTop */, "startActivityUnchecked");
                             }
 
                         }
@@ -3344,7 +3344,7 @@
             return false;
         }
 
-        stack.addTask(task, false, false);
+        stack.addTask(task, false, "restoreRecentTask");
         if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
                 "Added restored task=" + task + " to stack=" + stack);
         final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -3381,10 +3381,7 @@
         final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
         task.mResizeable = resizeable;
         mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
-        if (task.stack != null) {
-            task.stack.removeTask(task, reason, MOVING);
-        }
-        stack.addTask(task, toTop, MOVING);
+        stack.addTask(task, toTop, reason);
 
         // If the task had focus before (or we're requested to move focus),
         // move focus to the new stack.
@@ -3499,11 +3496,7 @@
 
         mWindowManager.positionTaskInStack(
                 taskId, stackId, position, task.mBounds, task.mOverrideConfig);
-        final boolean stackChanged = task.stack != null && task.stack != stack;
-        if (stackChanged) {
-            task.stack.removeTask(task, "moveTaskToStack", MOVING);
-        }
-        stack.positionTask(task, position, stackChanged);
+        stack.positionTask(task, position);
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4d18e96..5ee9eea 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -20,6 +20,7 @@
 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.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -1262,7 +1263,8 @@
         if (Objects.equals(mBounds, bounds)) {
             return null;
         }
-        Configuration oldConfig = mOverrideConfig;
+        final Configuration oldConfig = mOverrideConfig;
+        final boolean oldFullscreen = mFullscreen;
 
         mFullscreen = bounds == null;
         if (mFullscreen) {
@@ -1296,9 +1298,43 @@
                             ? Configuration.ORIENTATION_PORTRAIT
                             : Configuration.ORIENTATION_LANDSCAPE;
         }
+
+        if (mFullscreen != oldFullscreen) {
+            reportMultiWindowModeChange();
+        }
+
         return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
     }
 
+    private void reportMultiWindowModeChange() {
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                try {
+                    // An activity is consider to be in multi-window mode if its task isn't
+                    // fullscreen.
+                    r.app.thread.scheduleMultiWindowModeChanged(r.appToken, !mFullscreen);
+                } catch (Exception e) {
+                    Slog.e(TAG, "TaskRecord.reportMultiWindowModeChange: ", e);
+                }
+            }
+        }
+    }
+
+    void reportPictureInPictureModeChange() {
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                try {
+                    r.app.thread.schedulePictureInPictureModeChanged(
+                            r.appToken, stack.mStackId == PINNED_STACK_ID);
+                } catch (Exception e) {
+                    Slog.e(TAG, "TaskRecord.reportMultiWindowModeChange: ", e);
+                }
+            }
+        }
+    }
+
     /** Updates the task's bounds and override configuration to match what is expected for the
      * input stack. */
     void updateOverrideConfigurationForStack(ActivityStack inStack) {