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/api/current.txt b/api/current.txt
index 73e608a..c285eaa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3655,6 +3655,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMovedToDisplay(int);
     method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
@@ -44789,6 +44790,7 @@
     method public boolean onKeyUp(int, android.view.KeyEvent);
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
+    method public void onMovedToDisplay(int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2135d0c..225066b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3779,6 +3779,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMovedToDisplay(int);
     method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
@@ -48229,6 +48230,7 @@
     method public boolean onKeyUp(int, android.view.KeyEvent);
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
+    method public void onMovedToDisplay(int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
diff --git a/api/test-current.txt b/api/test-current.txt
index cd7ba58..486f5ac 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3657,6 +3657,7 @@
     method public void onLowMemory();
     method public boolean onMenuItemSelected(int, android.view.MenuItem);
     method public boolean onMenuOpened(int, android.view.Menu);
+    method public void onMovedToDisplay(int);
     method public void onMultiWindowModeChanged(boolean);
     method public boolean onNavigateUp();
     method public boolean onNavigateUpFromChild(android.app.Activity);
@@ -45099,6 +45100,7 @@
     method public boolean onKeyUp(int, android.view.KeyEvent);
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
+    method public void onMovedToDisplay(int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4449454..c56772e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1980,6 +1980,29 @@
         }
     }
 
+    void dispatchMovedToDisplay(int displayId) {
+        updateDisplay(displayId);
+        onMovedToDisplay(displayId);
+    }
+
+    /**
+     * Called by the system when the activity is moved from one display to another without
+     * recreation. This means that this activity is declared to handle all changes to configuration
+     * that happened when it was switched to another display, so it wasn't destroyed and created
+     * again. This call will be followed by {@link #onConfigurationChanged(Configuration)} if the
+     * applied configuration actually changed.
+     *
+     * <p>Use this callback to track changes to the displays if some activity functionality relies
+     * on an association with some display properties.
+     *
+     * @param displayId The id of the display to which activity was moved.
+     *
+     * @see #onConfigurationChanged(Configuration)
+     * @see View#onMovedToDisplay(int)
+     */
+    public void onMovedToDisplay(int displayId) {
+    }
+
     /**
      * Called by the system when the device configuration changes while your
      * activity is running.  Note that this will <em>only</em> be called if
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1f8e6db..be70dcd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.assist.AssistContent;
@@ -1017,9 +1019,16 @@
 
         @Override
         public void scheduleActivityConfigurationChanged(
-                IBinder token, Configuration overrideConfig, boolean reportToActivity) {
+                IBinder token, Configuration overrideConfig) {
             sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED,
-                    new ActivityConfigChangeData(token, overrideConfig), reportToActivity ? 1 : 0);
+                    new ActivityConfigChangeData(token, overrideConfig));
+        }
+
+        @Override
+        public void scheduleActivityMovedToDisplay(IBinder token, int displayId,
+                Configuration overrideConfig) {
+            sendMessage(H.ACTIVITY_MOVED_TO_DISPLAY,
+                    new ActivityConfigChangeData(token, overrideConfig), displayId);
         }
 
         @Override
@@ -1521,6 +1530,7 @@
         public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
         public static final int ATTACH_AGENT = 155;
         public static final int APPLICATION_INFO_CHANGED = 156;
+        public static final int ACTIVITY_MOVED_TO_DISPLAY = 157;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1550,6 +1560,7 @@
                     case DUMP_SERVICE: return "DUMP_SERVICE";
                     case LOW_MEMORY: return "LOW_MEMORY";
                     case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
+                    case ACTIVITY_MOVED_TO_DISPLAY: return "ACTIVITY_MOVED_TO_DISPLAY";
                     case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                     case PROFILER_CONTROL: return "PROFILER_CONTROL";
                     case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
@@ -1735,7 +1746,13 @@
                 case ACTIVITY_CONFIGURATION_CHANGED:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
                     handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
-                            msg.arg1 == 1 ? REPORT_TO_ACTIVITY : !REPORT_TO_ACTIVITY);
+                            INVALID_DISPLAY);
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    break;
+                case ACTIVITY_MOVED_TO_DISPLAY:
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
+                    handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
+                            msg.arg1 /* displayId */);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case PROFILER_CONTROL:
@@ -3775,7 +3792,7 @@
             if (!r.activity.mFinished && willBeVisible
                     && r.activity.mDecor != null && !r.hideForNow) {
                 if (r.newConfig != null) {
-                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
+                    performConfigurationChangedForActivity(r, r.newConfig);
                     if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                             + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
                     r.newConfig = null;
@@ -4129,7 +4146,7 @@
                     }
                 }
                 if (r.newConfig != null) {
-                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
+                    performConfigurationChangedForActivity(r, r.newConfig);
                     if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis "
                             + r.activityInfo.name + " with new config "
                             + r.activity.mCurrentConfig);
@@ -4842,17 +4859,32 @@
      * @param r ActivityClientRecord representing the Activity.
      * @param newBaseConfig The new configuration to use. This may be augmented with
      *                      {@link ActivityClientRecord#overrideConfig}.
-     * @param reportToActivity true if the change should be reported to the Activity's callback.
      */
     private void performConfigurationChangedForActivity(ActivityClientRecord r,
-                                                        Configuration newBaseConfig,
-                                                        boolean reportToActivity) {
+            Configuration newBaseConfig) {
+        performConfigurationChangedForActivity(r, newBaseConfig,
+                r.activity.getDisplay().getDisplayId(), false /* movedToDifferentDisplay */);
+    }
+
+    /**
+     * Updates the configuration for an Activity. The ActivityClientRecord's
+     * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
+     * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
+     * the updated Configuration.
+     * @param r ActivityClientRecord representing the Activity.
+     * @param newBaseConfig The new configuration to use. This may be augmented with
+     *                      {@link ActivityClientRecord#overrideConfig}.
+     * @param displayId The id of the display where the Activity currently resides.
+     * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
+     */
+    private void performConfigurationChangedForActivity(ActivityClientRecord r,
+            Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) {
         r.tmpConfig.setTo(newBaseConfig);
         if (r.overrideConfig != null) {
             r.tmpConfig.updateFrom(r.overrideConfig);
         }
-        performConfigurationChanged(r.activity, r.token, r.tmpConfig, r.overrideConfig,
-                reportToActivity);
+        performActivityConfigurationChanged(r.activity, r.tmpConfig, r.overrideConfig, displayId,
+                movedToDifferentDisplay);
         freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
     }
 
@@ -4873,37 +4905,58 @@
     }
 
     /**
-     * Decides whether to update an Activity's configuration and whether to tell the
-     * Activity/Component about it.
+     * Decides whether to update a component's configuration and whether to inform it.
      * @param cb The component callback to notify of configuration change.
-     * @param activityToken The Activity binder token for which this configuration change happened.
-     *                      If the change is global, this is null.
+     * @param newConfig The new configuration.
+     */
+    private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) {
+        if (!REPORT_TO_ACTIVITY) {
+            return;
+        }
+
+        // ContextThemeWrappers may override the configuration for that context. We must check and
+        // apply any overrides defined.
+        Configuration contextThemeWrapperOverrideConfig = null;
+        if (cb instanceof ContextThemeWrapper) {
+            final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
+            contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
+        }
+
+        // Apply the ContextThemeWrapper override if necessary.
+        // NOTE: Make sure the configurations are not modified, as they are treated as immutable
+        // in many places.
+        final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
+                newConfig, contextThemeWrapperOverrideConfig);
+        cb.onConfigurationChanged(configToReport);
+    }
+
+    /**
+     * Decides whether to update an Activity's configuration and whether to inform it.
+     * @param activity The activity to notify of configuration change.
      * @param newConfig The new configuration.
      * @param amOverrideConfig The override config that differentiates the Activity's configuration
-     *                       from the base global configuration.
-     *                       This is supplied by ActivityManager.
-     * @param reportToActivity Notify the Activity of the change.
+     *                         from the base global configuration. This is supplied by
+     *                         ActivityManager.
+     * @param displayId Id of the display where activity currently resides.
+     * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
      */
-    private void performConfigurationChanged(ComponentCallbacks2 cb,
-                                             IBinder activityToken,
-                                             Configuration newConfig,
-                                             Configuration amOverrideConfig,
-                                             boolean reportToActivity) {
-        // Only for Activity objects, check that they actually call up to their
-        // superclass implementation.  ComponentCallbacks2 is an interface, so
-        // we check the runtime type and act accordingly.
-        Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
-        if (activity != null) {
-            activity.mCalled = false;
+    private void performActivityConfigurationChanged(Activity activity, Configuration newConfig,
+            Configuration amOverrideConfig, int displayId, boolean movedToDifferentDisplay) {
+        if (activity == null) {
+            throw new IllegalArgumentException("No activity provided.");
+        }
+        final IBinder activityToken = activity.getActivityToken();
+        if (activityToken == null) {
+            throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
         }
 
         boolean shouldChangeConfig = false;
-        if ((activity == null) || (activity.mCurrentConfig == null)) {
+        if (activity.mCurrentConfig == null) {
             shouldChangeConfig = true;
         } else {
-            // If the new config is the same as the config this Activity is already
-            // running with and the override config also didn't change, then don't
-            // bother calling onConfigurationChanged.
+            // If the new config is the same as the config this Activity is already running with and
+            // the override config also didn't change, then don't bother calling
+            // onConfigurationChanged.
             int diff = activity.mCurrentConfig.diff(newConfig);
             if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
                     amOverrideConfig)) {
@@ -4912,53 +4965,57 @@
                 // calling onConfigurationChanged as we're going to destroy it.
                 if (!mUpdatingSystemConfig
                         || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
-                        || !reportToActivity) {
+                        || !REPORT_TO_ACTIVITY) {
                     shouldChangeConfig = true;
                 }
             }
         }
+        if (!shouldChangeConfig && !movedToDifferentDisplay) {
+            // Nothing significant, don't proceed with updating and reporting.
+            return;
+        }
+
+        // Propagate the configuration change to ResourcesManager and Activity.
+
+        // ContextThemeWrappers may override the configuration for that context. We must check and
+        // apply any overrides defined.
+        Configuration contextThemeWrapperOverrideConfig = activity.getOverrideConfiguration();
+
+        // We only update an Activity's configuration if this is not a global configuration change.
+        // This must also be done before the callback, or else we violate the contract that the new
+        // resources are available in ComponentCallbacks2#onConfigurationChanged(Configuration).
+        // Also apply the ContextThemeWrapper override if necessary.
+        // NOTE: Make sure the configurations are not modified, as they are treated as immutable in
+        // many places.
+        final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
+                amOverrideConfig, contextThemeWrapperOverrideConfig);
+        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig,
+                displayId, movedToDifferentDisplay);
+
+        activity.mConfigChangeFlags = 0;
+        activity.mCurrentConfig = new Configuration(newConfig);
+
+        if (!REPORT_TO_ACTIVITY) {
+            // Not configured to report to activity.
+            return;
+        }
+
+        if (movedToDifferentDisplay) {
+            activity.dispatchMovedToDisplay(displayId);
+        }
 
         if (shouldChangeConfig) {
-            // Propagate the configuration change to the Activity and ResourcesManager.
+            // Apply the ContextThemeWrapper override if necessary.
+            // NOTE: Make sure the configurations are not modified, as they are treated as immutable
+            // in many places.
+            final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
+                    newConfig, contextThemeWrapperOverrideConfig);
 
-            // ContextThemeWrappers may override the configuration for that context.
-            // We must check and apply any overrides defined.
-            Configuration contextThemeWrapperOverrideConfig = null;
-            if (cb instanceof ContextThemeWrapper) {
-                final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
-                contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
-            }
-
-            // We only update an Activity's configuration if this is not a global
-            // configuration change. This must also be done before the callback,
-            // or else we violate the contract that the new resources are available
-            // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
-            if (activityToken != null) {
-                // Apply the ContextThemeWrapper override if necessary.
-                // NOTE: Make sure the configurations are not modified, as they are treated
-                // as immutable in many places.
-                final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
-                        amOverrideConfig, contextThemeWrapperOverrideConfig);
-                mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig);
-            }
-
-            if (reportToActivity) {
-                // Apply the ContextThemeWrapper override if necessary.
-                // NOTE: Make sure the configurations are not modified, as they are treated
-                // as immutable in many places.
-                final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
-                        newConfig, contextThemeWrapperOverrideConfig);
-                cb.onConfigurationChanged(configToReport);
-            }
-
-            if (activity != null) {
-                if (reportToActivity && !activity.mCalled) {
-                    throw new SuperNotCalledException(
-                            "Activity " + activity.getLocalClassName() +
-                            " did not call through to super.onConfigurationChanged()");
-                }
-                activity.mConfigChangeFlags = 0;
-                activity.mCurrentConfig = new Configuration(newConfig);
+            activity.mCalled = false;
+            activity.onConfigurationChanged(configToReport);
+            if (!activity.mCalled) {
+                throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
+                                " did not call through to super.onConfigurationChanged()");
             }
         }
     }
@@ -5036,9 +5093,9 @@
                     // config and avoid onConfigurationChanged if it hasn't changed.
                     Activity a = (Activity) cb;
                     performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
-                            config, REPORT_TO_ACTIVITY);
+                            config);
                 } else {
-                    performConfigurationChanged(cb, null, config, null, REPORT_TO_ACTIVITY);
+                    performConfigurationChanged(cb, config);
                 }
             }
         }
@@ -5102,18 +5159,43 @@
         }
     }
 
-    final void handleActivityConfigurationChanged(ActivityConfigChangeData data,
-            boolean reportToActivity) {
+    /**
+     * Handle new activity configuration and/or move to a different display.
+     * @param data Configuration update data.
+     * @param displayId Id of the display where activity was moved to, -1 if there was no move and
+     *                  value didn't change.
+     */
+    private void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
         ActivityClientRecord r = mActivities.get(data.activityToken);
+        // Check input params.
         if (r == null || r.activity == null) {
+            if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
             return;
         }
+        final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY;
+        if (movedToDifferentDisplay) {
+            if (r.activity.getDisplay().getDisplayId() == displayId) {
+                throw new IllegalArgumentException("Activity is already on the target display: "
+                        + displayId);
+            }
+        }
 
-        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
-                + r.activityInfo.name + ", with callback=" + reportToActivity);
-
+        // Perform updates.
         r.overrideConfig = data.overrideConfig;
-        performConfigurationChangedForActivity(r, mCompatConfiguration, reportToActivity);
+        if (movedToDifferentDisplay) {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
+                    + r.activityInfo.name + ", displayId=" + displayId
+                    + ", config=" + data.overrideConfig);
+
+            performConfigurationChangedForActivity(r, mCompatConfiguration, displayId,
+                    true /* movedToDifferentDisplay */);
+            final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
+            viewRoot.onMovedToDisplay(displayId);
+        } else {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
+                    + r.activityInfo.name + ", config=" + data.overrideConfig);
+            performConfigurationChangedForActivity(r, mCompatConfiguration);
+        }
         mSomeActivitiesChanged = true;
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 16989c0..e154d2b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2072,6 +2072,13 @@
     }
 
     @Override
+    public void updateDisplay(int displayId) {
+        final DisplayAdjustments displayAdjustments = mResources.getDisplayAdjustments();
+        mDisplay = mResourcesManager.getAdjustedDisplay(displayId,
+                displayAdjustments);
+    }
+
+    @Override
     public DisplayAdjustments getDisplayAdjustments(int displayId) {
         return mResources.getDisplayAdjustments();
     }
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 7378e2b..1735572 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -100,8 +100,9 @@
             int resultCode, in String data, in Bundle extras, boolean ordered,
             boolean sticky, int sendingUser, int processState);
     void scheduleLowMemory();
-    void scheduleActivityConfigurationChanged(IBinder token, in Configuration overrideConfig,
-            boolean reportToActivity);
+    void scheduleActivityConfigurationChanged(IBinder token, in Configuration overrideConfig);
+    void scheduleActivityMovedToDisplay(IBinder token, int displayId,
+            in Configuration overrideConfig);
     void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
             in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
             in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 94a8990..55f7df3 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -521,7 +521,8 @@
             }
 
             // Update any existing Activity Resources references.
-            updateResourcesForActivity(activityToken, overrideConfig);
+            updateResourcesForActivity(activityToken, overrideConfig, displayId,
+                    false /* movedToDifferentDisplay */);
 
             // Now request an actual Resources object.
             return getOrCreateResources(activityToken, key, classLoader);
@@ -687,9 +688,12 @@
      * still valid and will have the updated configuration.
      * @param activityToken The Activity token.
      * @param overrideConfig The configuration override to update.
+     * @param displayId Id of the display where activity currently resides.
+     * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
-            @Nullable Configuration overrideConfig) {
+            @Nullable Configuration overrideConfig, int displayId,
+            boolean movedToDifferentDisplay) {
         try {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                     "ResourcesManager#updateResourcesForActivity");
@@ -697,8 +701,9 @@
                 final ActivityResources activityResources =
                         getOrCreateActivityResourcesStructLocked(activityToken);
 
-                if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
-                    // They are the same, no work to do.
+                if (Objects.equals(activityResources.overrideConfig, overrideConfig)
+                        && !movedToDifferentDisplay) {
+                    // They are the same and no change of display id, no work to do.
                     return;
                 }
 
@@ -721,7 +726,7 @@
                             + Configuration.resourceQualifierString(oldConfig)
                             + " to newConfig="
                             + Configuration.resourceQualifierString(
-                            activityResources.overrideConfig),
+                            activityResources.overrideConfig) + " displayId=" + displayId,
                             here);
                 }
 
@@ -765,12 +770,12 @@
                     // Create the new ResourcesKey with the rebased override config.
                     final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
                             oldKey.mSplitResDirs,
-                            oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+                            oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
                             rebasedOverrideConfig, oldKey.mCompatInfo);
 
                     if (DEBUG) {
                         Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
-                                + " to newKey=" + newKey);
+                                + " to newKey=" + newKey + ", displayId=" + displayId);
                     }
 
                     ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 06f7303..44e6ac5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4429,6 +4429,11 @@
     public abstract Display getDisplay();
 
     /**
+     * @hide
+     */
+    public abstract void updateDisplay(int displayId);
+
+    /**
      * Indicates whether this Context is restricted.
      *
      * @return {@code true} if this Context is restricted, {@code false} otherwise.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 546bfc4..38cdbb4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -865,6 +865,14 @@
         return mBase.getDisplay();
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void updateDisplay(int displayId) {
+        mBase.updateDisplay(displayId);
+    }
+
     @Override
     public Context createDeviceProtectedStorageContext() {
         return mBase.createDeviceProtectedStorageContext();
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 64f0222..483a49b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -289,7 +289,7 @@
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                     Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                     Configuration newConfig, Rect backDropRect, boolean forceLayout,
-                    boolean alwaysConsumeNavBar) {
+                    boolean alwaysConsumeNavBar, int displayId) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0, outsets);
                 mCaller.sendMessage(msg);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 10b1e19..14b2abe 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -50,7 +50,7 @@
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
             in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
             in Configuration newConfig, in Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar);
+            boolean alwaysConsumeNavBar, int displayId);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 018be86..d2577d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -818,7 +818,7 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                 Configuration newConfig, Rect backDropRect, boolean forceLayout,
-                boolean alwaysConsumeNavBar) {
+                boolean alwaysConsumeNavBar, int displayId) {
             SurfaceView surfaceView = mSurfaceView.get();
             if (surfaceView != null) {
                 if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ef5dc33..97f46fa 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15869,6 +15869,32 @@
     }
 
     /**
+     * @see #onMovedToDisplay(int)
+     */
+    void dispatchMovedToDisplay(Display display) {
+        mAttachInfo.mDisplay = display;
+        mAttachInfo.mDisplayState = display.getState();
+        onMovedToDisplay(display.getDisplayId());
+    }
+
+    /**
+     * Called by the system when the hosting activity is moved from one display to another without
+     * recreation. This means that the activity is declared to handle all changes to configuration
+     * that happened when it was switched to another display, so it wasn't destroyed and created
+     * again. This call will be followed by {@link #onConfigurationChanged(Configuration)} if the
+     * applied configuration actually changed.
+     *
+     * <p>Use this callback to track changes to the displays if some functionality relies on an
+     * association with some display properties.
+     *
+     * @param displayId The id of the display to which the view was moved.
+     *
+     * @see #onConfigurationChanged(Configuration)
+     */
+    public void onMovedToDisplay(int displayId) {
+    }
+
+    /**
      * Return true if the application tag in the AndroidManifest has set "supportRtl" to true
      */
     private boolean hasRtlSupport() {
@@ -23756,7 +23782,7 @@
 
         final IBinder mWindowToken;
 
-        final Display mDisplay;
+        Display mDisplay;
 
         final Callbacks mRootCallbacks;
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index fd3ff82..6c926c5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3239,6 +3239,17 @@
         }
     }
 
+    @Override
+    void dispatchMovedToDisplay(Display display) {
+        super.dispatchMovedToDisplay(display);
+
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchMovedToDisplay(display);
+        }
+    }
+
     /** @hide */
     @Override
     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 20d8a11..bbf840f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1034,6 +1034,26 @@
         }
     };
 
+    /**
+     * Notify about move to a different display.
+     * @param displayId The id of the display where this view root is moved to.
+     *
+     * @hide
+     */
+    public void onMovedToDisplay(int displayId) {
+        if (mDisplay.getDisplayId() == displayId) {
+            return;
+        }
+
+        // Get new instance of display based on current display adjustments. It may be updated later
+        // if moving between the displays also involved a configuration change.
+        final DisplayAdjustments displayAdjustments = mView.getResources().getDisplayAdjustments();
+        mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId, displayAdjustments);
+        mAttachInfo.mDisplayState = mDisplay.getState();
+        // Internal state updated, now notify the view hierarchy.
+        mView.dispatchMovedToDisplay(mDisplay);
+    }
+
     void pokeDrawLockIfNeeded() {
         final int displayState = mAttachInfo.mDisplayState;
         if (mView != null && mAdded && mTraversalScheduled
@@ -3523,7 +3543,8 @@
                         && mPendingOutsets.equals(args.arg7)
                         && mPendingBackDropFrame.equals(args.arg8)
                         && args.arg4 == null
-                        && args.argi1 == 0) {
+                        && args.argi1 == 0
+                        && mDisplay.getDisplayId() == args.argi3) {
                     break;
                 }
                 } // fall through...
@@ -3531,6 +3552,10 @@
                 if (mAdded) {
                     SomeArgs args = (SomeArgs) msg.obj;
 
+                    if (mDisplay.getDisplayId() != args.argi3) {
+                        onMovedToDisplay(args.argi3);
+                    }
+
                     Configuration config = (Configuration) args.arg4;
                     if (config != null) {
                         updateConfiguration(config, false);
@@ -6138,7 +6163,7 @@
     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             Configuration newConfig, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar) {
+            boolean alwaysConsumeNavBar, int displayId) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -6176,6 +6201,7 @@
         args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeNavBar ? 1 : 0;
+        args.argi3 = displayId;
         msg.obj = args;
         mHandler.sendMessage(msg);
     }
@@ -7188,12 +7214,12 @@
         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) {
+                boolean alwaysConsumeNavBar, int displayId) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                         visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame,
-                        forceLayout, alwaysConsumeNavBar);
+                        forceLayout, alwaysConsumeNavBar, displayId);
             }
         }
 
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index dd91d2f..ce51dc4 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -40,7 +40,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) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 4e70bb1..b2ff927 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -197,7 +197,8 @@
 
         final Configuration overrideConfig = new Configuration();
         overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mResourcesManager.updateResourcesForActivity(activity1, overrideConfig);
+        mResourcesManager.updateResourcesForActivity(activity1, overrideConfig,
+                Display.DEFAULT_DISPLAY, false /* movedToDifferentDisplay */);
         assertSame(resources1, theme.getResources());
 
         // Make sure we can still access the data.
@@ -246,7 +247,8 @@
 
         // Now update the Activity base override, and both resources should update.
         config1.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mResourcesManager.updateResourcesForActivity(activity1, config1);
+        mResourcesManager.updateResourcesForActivity(activity1, config1, Display.DEFAULT_DISPLAY,
+                false /* movedToDifferentDisplay */);
 
         expectedConfig1.orientation = Configuration.ORIENTATION_LANDSCAPE;
         assertEquals(expectedConfig1, resources1.getConfiguration());
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 {
 
     }
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 9ffd92d..6bcd08a 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -739,6 +739,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void updateDisplay(int displayId) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public File[] getExternalFilesDirs(String type) {
         throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 16bf724..852ab94 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1888,6 +1888,11 @@
     }
 
     @Override
+    public void updateDisplay(int displayId) {
+        // pass
+    }
+
+    @Override
     public int getUserId() {
         return 0; // not used
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index a51ad2e..4689491 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -50,7 +50,7 @@
 
     @Override
     public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
-            boolean b, Configuration configuration, Rect rect7, boolean b2, boolean b3)
+            boolean b, Configuration configuration, Rect rect7, boolean b2, boolean b3, int i0)
             throws RemoteException {
         // pass for now.
     }