diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 47ccc2f0badbe..106f8ac92d05e 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -1109,10 +1109,16 @@ public class ResourcesManager { Slog.v(TAG, "Changing resources " + resourcesImpl + " config to: " + config); } - int displayId = key.mDisplayId; - final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); + tmpConfig.setTo(config); + // Apply the override configuration before setting the display adjustments to ensure that + // the process config does not override activity display adjustments. + final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); + if (hasOverrideConfiguration) { + tmpConfig.updateFrom(key.mOverrideConfiguration); + } + // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the // update internally. @@ -1121,17 +1127,23 @@ public class ResourcesManager { daj = new DisplayAdjustments(daj); daj.setCompatibilityInfo(compat); } + + final int displayId = key.mDisplayId; if (displayId == Display.DEFAULT_DISPLAY) { - daj.setConfiguration(config); + daj.setConfiguration(tmpConfig); } DisplayMetrics dm = getDisplayMetrics(displayId, daj); if (displayId != Display.DEFAULT_DISPLAY) { applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig); + + // Re-apply the override configuration to ensure that configuration contexts based on + // a display context (ex: createDisplayContext().createConfigurationContext()) have the + // correct override. + if (hasOverrideConfiguration) { + tmpConfig.updateFrom(key.mOverrideConfiguration); + } } - if (hasOverrideConfiguration) { - tmpConfig.updateFrom(key.mOverrideConfiguration); - } resourcesImpl.updateConfiguration(tmpConfig, dm, compat); } diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 34417e68f11c9..a93dacf470504 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -44,8 +44,11 @@ import android.app.servertransaction.StopActivityItem; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.view.Display; import android.view.View; @@ -366,6 +369,58 @@ public class ActivityThreadTest { }); } + @Test + public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() { + final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final Configuration oldActivityConfig = + new Configuration(activity.getResources().getConfiguration()); + final DisplayMetrics oldActivityMetrics = new DisplayMetrics(); + activity.getDisplay().getMetrics(oldActivityMetrics); + final Resources oldAppResources = activity.getApplication().getResources(); + final Configuration oldAppConfig = + new Configuration(oldAppResources.getConfiguration()); + final DisplayMetrics oldApplicationMetrics = new DisplayMetrics(); + oldApplicationMetrics.setTo(oldAppResources.getDisplayMetrics()); + assertEquals("Process config must match the top activity config by default", + 0, oldActivityConfig.diffPublicOnly(oldAppConfig)); + assertEquals("Process config must match the top activity config by default", + oldActivityMetrics, oldApplicationMetrics); + + // Update the application configuration separately from activity config + final Configuration newAppConfig = new Configuration(oldAppConfig); + newAppConfig.densityDpi += 100; + newAppConfig.screenHeightDp += 100; + final Rect newBounds = new Rect(newAppConfig.windowConfiguration.getAppBounds()); + newBounds.bottom += 100; + newAppConfig.windowConfiguration.setAppBounds(newBounds); + newAppConfig.windowConfiguration.setBounds(newBounds); + newAppConfig.seq++; + + final ActivityThread activityThread = activity.getActivityThread(); + activityThread.handleConfigurationChanged(newAppConfig); + + // Verify that application config update was applied, but didn't change activity config. + assertEquals("Activity config must not change if the process config changes", + oldActivityConfig, activity.getResources().getConfiguration()); + + final DisplayMetrics newActivityMetrics = new DisplayMetrics(); + activity.getDisplay().getMetrics(newActivityMetrics); + assertEquals("Activity display size must not change if the process config changes", + oldActivityMetrics, newActivityMetrics); + final Resources newAppResources = activity.getApplication().getResources(); + assertEquals("Application config must be updated", + newAppConfig, newAppResources.getConfiguration()); + final DisplayMetrics newApplicationMetrics = new DisplayMetrics(); + newApplicationMetrics.setTo(newAppResources.getDisplayMetrics()); + assertNotEquals("Application display size must be updated after config update", + oldApplicationMetrics, newApplicationMetrics); + assertNotEquals("Application display size must be updated after config update", + newActivityMetrics, newApplicationMetrics); + }); + } + @Test public void testResumeAfterNewIntent() { final Activity activity = mActivityTestRule.launchActivity(new Intent());