Merge "Improved timing of when system sends multi-window change event to app"
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 914776e..90a7da9 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
@@ -409,15 +410,40 @@
     }
 
     void scheduleConfigurationChanged(Configuration config, boolean reportToActivity) {
-        if (app != null && app.thread != null) {
-            try {
-                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + " " +
-                        "reportToActivity=" + reportToActivity + " and config: " + config);
-                app.thread.scheduleActivityConfigurationChanged(
-                        appToken, new Configuration(config), reportToActivity);
-            } catch (RemoteException e) {
-                // If process died, whatever.
-            }
+        if (app == null || app.thread == null) {
+            return;
+        }
+        try {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + " " +
+                    "reportToActivity=" + reportToActivity + " and config: " + config);
+            app.thread.scheduleActivityConfigurationChanged(
+                    appToken, new Configuration(config), reportToActivity);
+        } catch (RemoteException e) {
+            // If process died, whatever.
+        }
+    }
+
+    void scheduleMultiWindowChanged() {
+        if (task == null || task.stack == null || app == null || app.thread == null) {
+            return;
+        }
+        try {
+            // An activity is considered to be in multi-window mode if its task isn't fullscreen.
+            app.thread.scheduleMultiWindowChanged(appToken, !task.mFullscreen);
+        } catch (Exception e) {
+            // If process died, I don't care.
+        }
+    }
+
+    void schedulePictureInPictureChanged() {
+        if (task == null || task.stack == null || app == null || app.thread == null) {
+            return;
+        }
+        try {
+            app.thread.schedulePictureInPictureChanged(
+                    appToken, task.stack.mStackId == PINNED_STACK_ID);
+        } catch (Exception e) {
+            // If process died, no one cares.
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 0e970b3..4825d57 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4827,7 +4827,7 @@
 
     private void postAddTask(TaskRecord task, ActivityStack prevStack) {
         if (prevStack != null) {
-            task.reportPictureInPictureChangeIfNeeded(prevStack);
+            mStackSupervisor.scheduleReportPictureInPictureChangedIfNeeded(task, prevStack);
         } else if (task.voiceSession != null) {
             try {
                 task.voiceSession.taskStarted(task.intent, task.taskId);
@@ -4886,7 +4886,7 @@
         r.setTask(task, null);
         task.addActivityToTop(r);
         setAppTask(r, task);
-        task.reportPictureInPictureChangeIfNeeded(prevStack);
+        mStackSupervisor.scheduleReportPictureInPictureChangedIfNeeded(task, prevStack);
         moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
         if (wasResumed) {
             prevStack.mResumedActivity = null;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c41caa8..6bbc5d6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -203,6 +203,8 @@
     static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
     static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
     static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
+    static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
+    static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
 
     private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
@@ -322,6 +324,14 @@
     /** List of activities that are in the process of going to sleep. */
     final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
 
+    /** List of activities whose multi-window mode changed that we need to report to the
+     * application */
+    final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
+
+    /** List of activities whose picture-in-picture mode changed that we need to report to the
+     * application */
+    final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
+
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
 
@@ -3390,6 +3400,38 @@
         mActivityMetricsLogger.logWindowState();
     }
 
+    void scheduleReportMultiWindowChanged(TaskRecord task) {
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                mMultiWindowModeChangedActivities.add(r);
+            }
+        }
+
+        if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
+            mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
+        }
+    }
+
+    void scheduleReportPictureInPictureChangedIfNeeded(TaskRecord task, ActivityStack prevStack) {
+        final ActivityStack stack = task.stack;
+        if (prevStack == null || prevStack == stack
+                || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
+            return;
+        }
+
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.app != null && r.app.thread != null) {
+                mPipModeChangedActivities.add(r);
+            }
+        }
+
+        if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+        }
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {
@@ -3405,6 +3447,22 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
+                    synchronized (mService) {
+                        for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
+                            final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
+                            r.scheduleMultiWindowChanged();
+                        }
+                    }
+                } break;
+                case REPORT_PIP_MODE_CHANGED_MSG: {
+                    synchronized (mService) {
+                        for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
+                            final ActivityRecord r = mPipModeChangedActivities.remove(i);
+                            r.schedulePictureInPictureChanged();
+                        }
+                    }
+                } break;
                 case IDLE_TIMEOUT_MSG: {
                     if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                             "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f39ab2f..a7d948c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1320,7 +1320,7 @@
         }
 
         if (mFullscreen != oldFullscreen) {
-            reportMultiWindowChange();
+            mService.mStackSupervisor.scheduleReportMultiWindowChanged(this);
         }
 
         return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
@@ -1375,40 +1375,6 @@
         return bounds;
     }
 
-    private void reportMultiWindowChange() {
-        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.scheduleMultiWindowChanged(r.appToken, !mFullscreen);
-                } catch (Exception e) {
-                    Slog.e(TAG, "TaskRecord.reportMultiWindowChange: ", e);
-                }
-            }
-        }
-    }
-
-    void reportPictureInPictureChangeIfNeeded(ActivityStack prevStack) {
-        if (prevStack == null || prevStack == stack
-                || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
-            return;
-        }
-
-        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.schedulePictureInPictureChanged(
-                            r.appToken, stack.mStackId == PINNED_STACK_ID);
-                } catch (Exception e) {
-                    Slog.e(TAG, "TaskRecord.reportPictureInPictureChangeIfNeeded: ", e);
-                }
-            }
-        }
-    }
-
     /** Updates the task's bounds and override configuration to match what is expected for the
      * input stack. */
     void updateOverrideConfigurationForStack(ActivityStack inStack) {