diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1344727ab36da..66b305ec2dac5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1085,6 +1085,22 @@ final class ActivityRecord extends ConfigurationContainer { if (root == this) { task.setRootProcess(proc); } + // Override the process configuration to match the display where the first activity in + // the process was launched. This can help with compat issues on secondary displays when + // apps use Application to obtain configuration or metrics instead of Activity. + final ActivityDisplay display = getDisplay(); + if (display == null || display.mDisplayId == INVALID_DISPLAY) { + return; + } + if (!proc.hasActivities() && display.mDisplayId != DEFAULT_DISPLAY) { + proc.registerDisplayConfigurationListenerLocked(display); + } else if (display.mDisplayId == DEFAULT_DISPLAY) { + // Once an activity is launched on default display - stop listening for other displays + // configurations to maintain compatibility with previous platform releases. E.g. when + // an activity is launched in a Bubble and then moved to default screen, we should match + // the global device config. + proc.unregisterDisplayConfigurationListenerLocked(); + } } boolean hasProcess() { @@ -3233,7 +3249,7 @@ final class ActivityRecord extends ConfigurationContainer { // Update last reported values. final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration(); - setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig); + setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig); if (mState == INITIALIZING) { // No need to relaunch or schedule new config for activity that hasn't been launched @@ -3342,6 +3358,14 @@ final class ActivityRecord extends ConfigurationContainer { return true; } + /** Get process configuration, or global config if the process is not set. */ + private Configuration getProcessGlobalConfiguration() { + if (app != null) { + return app.getConfiguration(); + } + return mAtmService.getGlobalConfiguration(); + } + /** * When assessing a configuration change, decide if the changes flags and the new configurations * should cause the Activity to relaunch. @@ -3449,7 +3473,7 @@ final class ActivityRecord extends ConfigurationContainer { mStackSupervisor.activityRelaunchingLocked(this); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults, pendingNewIntents, configChangeFlags, - new MergedConfiguration(mAtmService.getGlobalConfiguration(), + new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()), preserveWindow); final ActivityLifecycleItem lifecycleItem; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 11a177a71a10a..f8fd64a8feb2c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -38,6 +40,7 @@ import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -56,10 +59,10 @@ import android.app.servertransaction.PauseActivityItem; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.util.MutableBoolean; +import android.view.DisplayInfo; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner.Stub; import android.view.RemoteAnimationAdapter; @@ -598,6 +601,67 @@ public class ActivityRecordTests extends ActivityTestsBase { assertNull(mActivity.pendingOptions); } + @Test + public void testSetProcessOverridesConfig() { + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, null /* process */); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + assertEquals(secondaryDisplayActivity.getDisplay().getResolvedOverrideConfiguration(), + secondaryDisplayActivity.app.getRequestedOverrideConfiguration()); + + assertNotEquals(defaultDisplayActivity.getConfiguration(), + secondaryDisplayActivity.getConfiguration()); + } + + @Test + public void testSetProcessDoesntOverrideConfigIfAnotherActivityPresent() { + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, null /* process */); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, defaultDisplayActivity.app); + assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + } + + @Test + public void testActivityOnDefaultDisplayClearsProcessOverride() { + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, + secondaryDisplayActivity.app); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + } + + /** + * Creates an activity on display. For non-default display request it will also create a new + * display with custom DisplayInfo. + */ + private ActivityRecord createActivityOnDisplay(boolean defaultDisplay, + WindowProcessController process) { + final ActivityDisplay display; + if (defaultDisplay) { + display = mRootActivityContainer.getDefaultDisplay(); + } else { + final DisplayInfo info = new DisplayInfo(); + info.logicalWidth = 100; + info.logicalHeight = 100; + display = addNewActivityDisplayAt(info, POSITION_TOP); + } + final TestActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); + return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build(); + } + /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */ private void prepareFixedAspectRatioUnresizableActivity() { setupDisplayContentForCompatDisplayInsets(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 53b0add8c37ee..d8c0de7418457 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -195,6 +195,7 @@ class ActivityTestsBase { private ActivityStack mStack; private int mActivityFlags; private int mLaunchMode; + private WindowProcessController mWpc; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -245,6 +246,11 @@ class ActivityTestsBase { return this; } + ActivityBuilder setUseProcess(WindowProcessController wpc) { + mWpc = wpc; + return this; + } + ActivityRecord build() { if (mComponent == null) { final int id = sCurrentActivityId++; @@ -290,12 +296,18 @@ class ActivityTestsBase { mTaskRecord.addActivityToTop(activity); } - final WindowProcessController wpc = new WindowProcessController(mService, - mService.mContext.getApplicationInfo(), "name", 12345, - UserHandle.getUserId(12345), mock(Object.class), - mock(WindowProcessListener.class)); - wpc.setThread(mock(IApplicationThread.class)); + final WindowProcessController wpc; + if (mWpc != null) { + wpc = mWpc; + } else { + wpc = new WindowProcessController(mService, + mService.mContext.getApplicationInfo(), "name", 12345, + UserHandle.getUserId(12345), mock(Object.class), + mock(WindowProcessListener.class)); + wpc.setThread(mock(IApplicationThread.class)); + } activity.setProcess(wpc); + wpc.addActivityIfNeeded(activity); return activity; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index fe45411830381..2c9074e933b65 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -153,7 +153,6 @@ public class AppWindowTokenTests extends WindowTestsBase { @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; mWm.mDisplayEnabled = true; final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( @@ -186,7 +185,6 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testLandscapeSeascapeRotationByPolicy() { // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; mWm.mDisplayEnabled = true; final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 366aceafd7bf4..427a92963807e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -41,6 +41,7 @@ import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.IntentFilter; +import android.content.res.Configuration; import android.database.ContentObserver; import android.hardware.display.DisplayManagerInternal; import android.net.Uri; @@ -175,6 +176,12 @@ public class SystemServicesTestRule implements TestRule { // Display creation is driven by the ActivityManagerService via // ActivityStackSupervisor. We emulate those steps here. mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + mWindowManagerService.displayReady(); + + final Configuration defaultDisplayConfig = + mWindowManagerService.computeNewConfiguration(DEFAULT_DISPLAY); + doReturn(defaultDisplayConfig).when(atms).getGlobalConfiguration(); + doReturn(defaultDisplayConfig).when(atms).getGlobalConfigurationForPid(anyInt()); mMockTracker.stopTracking(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 3a702cb9521ca..de28b5f0fa4ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -154,7 +154,6 @@ class WindowTestsBase { context.getDisplay().getDisplayInfo(mDisplayInfo); mDisplayContent = createNewDisplay(); mWm.mDisplayEnabled = true; - mWm.mDisplayReady = true; // Set-up some common windows. mCommonWindows = new HashSet<>();