Report move to display for activities that handle config changes

When activity that is moved between displays handles all configuration
changes, it won't be restarted. This CL adds a callback to the client
to notify it about display change. Usually it will be followed by
onConfigurationChanged, except when configuration didn't actually change.
When activity is recreated, it won't receive onMovedToDisplay.

Bug: 34862802
Test: android.server.cts.ActivityManagerDisplayTests
Test: #testOnMovedToDisplayCallback
Change-Id: I9a9501cab788623ada15a31efb53e4b2378639fe
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 082b6b5..78b6611 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -201,6 +201,7 @@
     // WARNING: Reference points to {@link TaskRecord#getMergedOverrideConfig}, so its internal
     // state should never be altered directly.
     private Configuration mLastReportedOverrideConfiguration;
+    private int mLastReportedDisplayId;
     CompatibilityInfo compat;// last used compatibility mode
     ActivityRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
@@ -513,16 +514,37 @@
         mSmallestSizeConfigurations = smallestSizeConfigurations;
     }
 
-    private void scheduleConfigurationChanged(Configuration config, boolean reportToActivity) {
+    private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
         if (app == null || app.thread == null) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
+                    "Can't report activity moved to display - client not running, activityRecord="
+                            + this + ", displayId=" + displayId);
             return;
         }
         try {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + " " +
-                    "reportToActivity=" + reportToActivity + " and config: " + config);
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Reporting activity moved to display" + ", activityRecord=" + this
+                            + ", displayId=" + displayId + ", config=" + config);
 
-            app.thread.scheduleActivityConfigurationChanged(appToken, new Configuration(config),
-                    reportToActivity);
+            app.thread.scheduleActivityMovedToDisplay(appToken, displayId,
+                    new Configuration(config));
+        } catch (RemoteException e) {
+            // If process died, whatever.
+        }
+    }
+
+    private void scheduleConfigurationChanged(Configuration config) {
+        if (app == null || app.thread == null) {
+            if (DEBUG_CONFIGURATION) Slog.w(TAG,
+                    "Can't report activity configuration update - client not running"
+                            + ", activityRecord=" + this);
+            return;
+        }
+        try {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+                    + config);
+
+            app.thread.scheduleActivityConfigurationChanged(appToken, new Configuration(config));
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -1969,26 +1991,31 @@
             return true;
         }
 
+        // We don't worry about activities that are finishing.
+        if (finishing) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Configuration doesn't matter in finishing " + this);
+            stopFreezingScreenLocked(false);
+            return true;
+        }
+
         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                 "Ensuring correct configuration: " + this);
 
+        final int newDisplayId = getDisplayId();
+        final boolean displayChanged = mLastReportedDisplayId != newDisplayId;
+        if (displayChanged) {
+            mLastReportedDisplayId = newDisplayId;
+        }
         // Short circuit: if the two full configurations are equal (the common case), then there is
         // nothing to do.  We test the full configuration instead of the global and merged override
         // configurations because there are cases (like moving a task to the pinned stack) where
         // the combine configurations are equal, but would otherwise differ in the override config
         mTmpConfig1.setTo(mLastReportedConfiguration);
         mTmpConfig1.updateFrom(mLastReportedOverrideConfiguration);
-        if (task.getConfiguration().equals(mTmpConfig1) && !forceNewConfig) {
+        if (task.getConfiguration().equals(mTmpConfig1) && !forceNewConfig && !displayChanged) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration unchanged in " + this);
-            return true;
-        }
-
-        // We don't worry about activities that are finishing.
-        if (finishing) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter in finishing " + this);
-            stopFreezingScreenLocked(false);
+                    "Configuration & display unchanged in " + this);
             return true;
         }
 
@@ -2009,7 +2036,11 @@
                     "Configuration no differences in " + this);
             // There are no significant differences, so we won't relaunch but should still deliver
             // the new configuration to the client process.
-            scheduleConfigurationChanged(newTaskMergedOverrideConfig, true);
+            if (displayChanged) {
+                scheduleActivityMovedToDisplay(newDisplayId, newTaskMergedOverrideConfig);
+            } else {
+                scheduleConfigurationChanged(newTaskMergedOverrideConfig);
+            }
             return true;
         }
 
@@ -2082,7 +2113,11 @@
         // NOTE: We only forward the task override configuration as the system level configuration
         // changes is always sent to all processes when they happen so it can just use whatever
         // system level configuration it last got.
-        scheduleConfigurationChanged(newTaskMergedOverrideConfig, true);
+        if (displayChanged) {
+            scheduleActivityMovedToDisplay(newDisplayId, newTaskMergedOverrideConfig);
+        } else {
+            scheduleConfigurationChanged(newTaskMergedOverrideConfig);
+        }
         stopFreezingScreenLocked(false);
 
         return true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 477bb7f..d250827 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2730,6 +2730,15 @@
             if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
                     + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
                     + Integer.toHexString(theme));
+
+            // Obtain proper context to launch on the right display.
+            final Context displayContext = getDisplayContext(context, displayId);
+            if (displayContext == null) {
+                // Can't show splash screen on requested display, so skip showing at all.
+                return null;
+            }
+            context = displayContext;
+
             if (theme != context.getThemeResId() || labelRes != 0) {
                 try {
                     context = context.createPackageContext(packageName, 0);
@@ -2814,14 +2823,7 @@
             }
 
             params.setTitle("Splash Screen " + packageName);
-
-            // Obtain proper context to launch on the right display.
-            final Context displayContext = getDisplayContext(context, displayId);
-            if (displayContext == null) {
-                // Can't show splash screen on requested display, so skip showing at all.
-                return null;
-            }
-            wm = (WindowManager) displayContext.getSystemService(WINDOW_SERVICE);
+            wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
             view = win.getDecorView();
 
             if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index cfcbbd0..9f52412 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -226,7 +226,8 @@
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
                 Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
-                Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar) {
+                Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar,
+                int displayId) {
             if (reportDraw) {
                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 65e1f84..7de6856 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3027,6 +3027,7 @@
             final Rect outsets = mLastOutsets;
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
+            final int displayId = getDisplayId();
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -3036,7 +3037,7 @@
                         try {
                             dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                     stableInsets, outsets, reportDraw, newConfig,
-                                    reportOrientation);
+                                    reportOrientation, displayId);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
@@ -3044,7 +3045,7 @@
                 });
             } else {
                 dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                        outsets, reportDraw, newConfig, reportOrientation);
+                        outsets, reportDraw, newConfig, reportOrientation, displayId);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -3101,13 +3102,14 @@
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            Configuration newConfig, boolean reportOrientation) throws RemoteException {
+            Configuration newConfig, boolean reportOrientation, int displayId)
+            throws RemoteException {
         final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
                 || reportOrientation;
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, newConfig, getBackdropFrame(frame),
-                forceRelayout, mPolicy.isNavBarForcedShownLw(this));
+                forceRelayout, mPolicy.isNavBarForcedShownLw(this), displayId);
         mDragResizingChangeReported = true;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index c029a9f..b6dc9a5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -37,7 +37,7 @@
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar)
+            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
             throws RemoteException {
 
     }