Merge changes I14c90b6a,I7850a556
* changes:
Reset configuration of invisible size-compat activity if display changes
Don't involve decor inset into app bounds of fixed aspect ratio app
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4706930..d1faa71 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
@@ -2710,22 +2711,50 @@
if (!shouldUseSizeCompatMode()) {
return false;
}
- final Configuration parentConfig = getParent().getConfiguration();
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ if (resolvedAppBounds == null) {
+ // The override configuration has not been resolved yet.
+ return false;
+ }
+
+ final Configuration parentConfig = getParent().getConfiguration();
// Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
// fields should be changed with density and bounds, so here only compares the most
// significant field.
if (parentConfig.densityDpi != resolvedConfig.densityDpi) {
return true;
}
+
final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
- final Rect parentBounds = parentAppBounds != null
- ? parentAppBounds : parentConfig.windowConfiguration.getBounds();
- final Rect overrideBounds = resolvedConfig.windowConfiguration.getBounds();
+ final int appWidth = resolvedAppBounds.width();
+ final int appHeight = resolvedAppBounds.height();
+ final int parentAppWidth = parentAppBounds.width();
+ final int parentAppHeight = parentAppBounds.height();
+ if (parentAppWidth < appWidth || parentAppHeight < appHeight) {
+ // One side is larger than the parent.
+ return true;
+ }
+
+ if (info.hasFixedAspectRatio()) {
+ final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
+ / Math.min(appWidth, appHeight);
+ final float parentAspectRatio = (0.5f + Math.max(parentAppWidth, parentAppHeight))
+ / Math.min(parentAppWidth, parentAppHeight);
+ // Check if the parent still has available space in long side.
+ if (aspectRatio < parentAspectRatio
+ && (aspectRatio < info.maxAspectRatio || info.minAspectRatio > 0)) {
+ return true;
+ }
+ }
+
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// If the width or height is the same as parent, it is already the best fit of the override
- // bounds, therefore this condition is considered as not size compatibility mode.
- return parentBounds.width() != overrideBounds.width()
- && parentBounds.height() != overrideBounds.height();
+ // bounds, therefore this condition is considered as not size compatibility mode. Here uses
+ // right and bottom as width and height of parent because the bounds may contain decor
+ // insets which has been accounted in override bounds. See {@link #computeBounds}.
+ return parentAppBounds.right != resolvedBounds.width()
+ && parentAppBounds.bottom != resolvedBounds.height();
}
/**
@@ -2778,15 +2807,18 @@
// are relative to bounds and density, they will be calculated in
// {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
// relatively fixed.
- final Configuration srcConfig = task.getConfiguration();
- overrideConfig.colorMode = srcConfig.colorMode;
- overrideConfig.densityDpi = srcConfig.densityDpi;
- overrideConfig.screenLayout = srcConfig.screenLayout
+ final Configuration parentConfig = task.getConfiguration();
+ // Don't account decor insets into app bounds.
+ mTmpBounds.intersect(parentConfig.windowConfiguration.getAppBounds());
+ overrideConfig.windowConfiguration.setAppBounds(mTmpBounds);
+ overrideConfig.colorMode = parentConfig.colorMode;
+ overrideConfig.densityDpi = parentConfig.densityDpi;
+ overrideConfig.screenLayout = parentConfig.screenLayout
& (Configuration.SCREENLAYOUT_LONG_MASK
| Configuration.SCREENLAYOUT_SIZE_MASK);
// The smallest screen width is the short side of screen bounds. Because the bounds
// and density won't be changed, smallestScreenWidthDp is also fixed.
- overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp;
+ overrideConfig.smallestScreenWidthDp = parentConfig.smallestScreenWidthDp;
}
}
onRequestedOverrideConfigurationChanged(overrideConfig);
@@ -2794,33 +2826,38 @@
@Override
void resolveOverrideConfiguration(Configuration newParentConfiguration) {
- super.resolveOverrideConfiguration(newParentConfiguration);
+ // If the activity has override bounds, the relative configuration (e.g. screen size,
+ // layout) needs to be resolved according to the bounds.
+ final boolean hasOverrideBounds = !matchParentBounds();
+ if (hasOverrideBounds && shouldUseSizeCompatMode()) {
+ resolveSizeCompatModeConfiguration(newParentConfiguration);
+ } else {
+ super.resolveOverrideConfiguration(newParentConfiguration);
+ if (hasOverrideBounds) {
+ task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
+ newParentConfiguration, true /* insideParentBounds */);
+ }
+ }
// Assign configuration sequence number into hierarchy because there is a different way than
// ensureActivityConfiguration() in this class that uses configuration in WindowState during
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+ }
- if (matchParentBounds()) {
- return;
- }
-
+ private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- if (!shouldUseSizeCompatMode()) {
- computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
- ORIENTATION_UNDEFINED, true /* insideParentBounds */);
- return;
- }
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- final Configuration displayConfig = getDisplay().getConfiguration();
int orientation = getConfiguration().orientation;
- if (orientation != displayConfig.orientation && isConfigurationCompatible(displayConfig)) {
+ if (orientation != newParentConfiguration.orientation
+ && isConfigurationCompatible(newParentConfiguration)) {
// The activity is compatible to apply the orientation change or it requests different
// fixed orientation.
- orientation = displayConfig.orientation;
+ orientation = newParentConfiguration.orientation;
} else {
- if (resolvedConfig.windowConfiguration.getAppBounds() != null) {
+ if (!resolvedBounds.isEmpty()) {
// Keep the computed resolved override configuration.
return;
}
@@ -2830,35 +2867,55 @@
}
}
- // Adjust the bounds to match the current orientation.
- if (orientation != ORIENTATION_UNDEFINED) {
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- final int longSide = Math.max(resolvedBounds.height(), resolvedBounds.width());
- final int shortSide = Math.min(resolvedBounds.height(), resolvedBounds.width());
- final boolean toBeLandscape = orientation == ORIENTATION_LANDSCAPE;
- final int width = toBeLandscape ? longSide : shortSide;
- final int height = toBeLandscape ? shortSide : longSide;
- // Assume the bounds is always started from zero because the size may be bigger than its
+ // The requested override bounds will set to the resolved bounds.
+ super.resolveOverrideConfiguration(newParentConfiguration);
+
+ boolean shouldSwapAppBounds = false;
+ int width = resolvedBounds.width();
+ int height = resolvedBounds.height();
+ if ((orientation == ORIENTATION_LANDSCAPE && height > width)
+ || (orientation == ORIENTATION_PORTRAIT && width > height)) {
+ // Swap width and height because they are opposite to the orientation.
+ width = resolvedBounds.height();
+ height = resolvedBounds.width();
+ // Assume the bounds always starts from zero because the size may be larger than its
// parent (task ~ display). The actual letterboxing will be done by surface offset.
resolvedBounds.set(0, 0, width, height);
+ shouldSwapAppBounds = true;
+ } else if (width == height) {
+ // The bounds may contain decor insets, then its app bounds may not be 1:1 and need to
+ // be adjusted according to the orientation.
+ final int appWidth = resolvedConfig.windowConfiguration.getAppBounds().width();
+ final int appHeight = resolvedConfig.windowConfiguration.getAppBounds().height();
+ shouldSwapAppBounds = (orientation == ORIENTATION_LANDSCAPE && appHeight > appWidth)
+ || (orientation == ORIENTATION_PORTRAIT && appWidth > appHeight);
}
- // In size compatible mode, activity is allowed to have larger bounds than its parent.
- computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, orientation,
+ final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ if (shouldSwapAppBounds) {
+ // Preserve the original decor insets (the left and top of the resolved app bounds) if
+ // the parent also has the insets at the corresponding side.
+ final int left = parentAppBounds.left > 0 ? resolvedAppBounds.top : 0;
+ final int top = parentAppBounds.top > 0 ? resolvedAppBounds.left : 0;
+ final int appWidth = resolvedAppBounds.height();
+ final int appHeight = resolvedAppBounds.width();
+ resolvedAppBounds.set(left, top, appWidth + left, appHeight + top);
+ }
+ // The horizontal inset included in width is not needed if the activity cannot fill the
+ // parent, because the offset will be applied by {@link AppWindowToken#mSizeCompatBounds}.
+ if (resolvedBounds.width() < parentAppBounds.width()) {
+ resolvedBounds.right -= resolvedAppBounds.left;
+ }
+
+ // In size compatibility mode, activity is allowed to have larger bounds than its parent.
+ task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
false /* insideParentBounds */);
- }
-
- private void computeConfigResourceOverrides(Configuration inOutConfig,
- Configuration parentConfig, int orientation, boolean insideParentBounds) {
- // Set the real orientation or undefined value to ensure the output orientation won't be the
- // old value. Also reset app bounds so it will be updated according to bounds.
- inOutConfig.orientation = orientation;
- final Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds != null) {
- outAppBounds.setEmpty();
+ // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
+ // the parent bounds appropriately.
+ if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
+ resolvedConfig.orientation = newParentConfiguration.orientation;
}
-
- task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds);
}
@Override
@@ -2889,8 +2946,27 @@
}
final ActivityDisplay display = getDisplay();
- if (display != null) {
+ if (display == null) {
+ return;
+ }
+ if (visible) {
+ // It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
+ } else if (shouldUseSizeCompatMode()) {
+ // The override changes can only be obtained from display, because we don't have the
+ // difference of full configuration in each hierarchy.
+ final int displayChanges = display.getLastOverrideConfigurationChanges();
+ final int orientationChanges = CONFIG_WINDOW_CONFIGURATION
+ | CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION;
+ final boolean hasNonOrienSizeChanged = hasResizeChange(displayChanges)
+ // Filter out the case of simple orientation change.
+ && (displayChanges & orientationChanges) != orientationChanges;
+ // For background activity that uses size compatibility mode, if the size or density of
+ // the display is changed, then reset the override configuration and kill the activity's
+ // process if its process state is not important to user.
+ if (hasNonOrienSizeChanged || (displayChanges & ActivityInfo.CONFIG_DENSITY) != 0) {
+ restartProcessIfVisible();
+ }
}
}
@@ -2990,14 +3066,14 @@
// {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
// the method has run before, then effect of {@link #getRequestedOverrideBounds()} will
// already have been applied to the value returned from {@link getConfiguration}. Refer
- // to {@link TaskRecord#computeOverrideConfiguration}.
+ // to {@link TaskRecord#computeConfigResourceOverrides()}.
outBounds.set(getRequestedOverrideBounds());
return;
}
// Compute configuration based on max supported width and height.
// Also account for the left / top insets (e.g. from display cutouts), which will be clipped
- // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
+ // away later in {@link TaskRecord#computeConfigResourceOverrides()}. Otherwise, the app
// bounds would end up too small.
outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top);
}
@@ -3357,7 +3433,8 @@
void restartProcessIfVisible() {
Slog.i(TAG, "Request to restart process of " + this);
- // Reset the existing override configuration to the latest configuration.
+ // Reset the existing override configuration so it can be updated according to the latest
+ // configuration.
getRequestedOverrideConfiguration().setToDefaults();
getResolvedOverrideConfiguration().setToDefaults();
if (visible) {
@@ -3377,8 +3454,17 @@
// Kill its process immediately because the activity should be in background.
// The activity state will be update to {@link #DESTROYED} in
// {@link ActivityStack#cleanUpActivityLocked} when handling process died.
- mAtmService.mH.post(() -> mAtmService.mAmInternal.killProcess(
- app.mName, app.mUid, "restartActivityProcess"));
+ mAtmService.mH.post(() -> {
+ final WindowProcessController wpc;
+ synchronized (mAtmService.mGlobalLock) {
+ if (!hasProcess()
+ || app.getReportedProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ return;
+ }
+ wpc = app;
+ }
+ mAtmService.mAmInternal.killProcess(wpc.mName, wpc.mUid, "resetConfig");
+ });
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b8e6b0c..7ea7cf1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5209,7 +5209,7 @@
// the ATMS lock held.
final Message msg = PooledLambda.obtainMessage(
ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
- N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ N, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
mH.sendMessage(msg);
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 955f2e9..1e1c482 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1627,7 +1627,8 @@
// If the changes come from change-listener, the incoming parent configuration is
// still the old one. Make sure their orientations are the same to reduce computing
// the compatibility bounds for the intermediate state.
- && getResolvedOverrideConfiguration().orientation == newParentConfig.orientation) {
+ && (task.mTaskRecord == null || task.mTaskRecord
+ .getConfiguration().orientation == newParentConfig.orientation)) {
final Rect taskBounds = task.getBounds();
// Since we only center the activity horizontally, if only the fixed height is smaller
// than its container, the override bounds don't need to take effect.
@@ -1763,7 +1764,8 @@
final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
final Rect viewportBounds = parentAppBounds != null
? parentAppBounds : newParentConfig.windowConfiguration.getBounds();
- final Rect contentBounds = getResolvedOverrideBounds();
+ final Rect appBounds = getWindowConfiguration().getAppBounds();
+ final Rect contentBounds = appBounds != null ? appBounds : getResolvedOverrideBounds();
final float contentW = contentBounds.width();
final float contentH = contentBounds.height();
final float viewportW = viewportBounds.width();
@@ -1780,6 +1782,8 @@
mSizeCompatBounds.set(contentBounds);
mSizeCompatBounds.offsetTo(0, 0);
mSizeCompatBounds.scale(mSizeCompatScale);
+ // The decor inset is included in height.
+ mSizeCompatBounds.bottom += viewportBounds.top;
mSizeCompatBounds.left += offsetX;
mSizeCompatBounds.right += offsetX;
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 5c528c7..3886ae1 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -77,6 +77,9 @@
*/
private Configuration mFullConfiguration = new Configuration();
+ /** The bit mask of the last override fields of full configuration. */
+ private int mLastOverrideConfigurationChanges;
+
/**
* Contains merged override configuration settings from the top of the hierarchy down to this
* particular instance. It is different from {@link #mFullConfiguration} because it starts from
@@ -108,6 +111,11 @@
return mFullConfiguration;
}
+ /** Returns the last changes from applying override configuration. */
+ int getLastOverrideConfigurationChanges() {
+ return mLastOverrideConfigurationChanges;
+ }
+
/**
* Notify that parent config changed and we need to update full configuration.
* @see #mFullConfiguration
@@ -116,7 +124,8 @@
mTmpConfig.setTo(mResolvedOverrideConfiguration);
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
- mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+ mLastOverrideConfigurationChanges =
+ mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
if (!mTmpConfig.equals(mResolvedOverrideConfiguration)) {
onMergedOverrideConfigurationChanged();
// This depends on the assumption that change-listeners don't do
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 4fd8489..714c227 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -2115,8 +2115,9 @@
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, bounds, di);
} else {
- mTmpNonDecorBounds.set(bounds);
- mTmpStableBounds.set(bounds);
+ // Set to app bounds because it excludes decor insets.
+ mTmpNonDecorBounds.set(outAppBounds);
+ mTmpStableBounds.set(outAppBounds);
}
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 85e8a14..63b9198 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -350,7 +350,7 @@
activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- activity.getTaskRecord().getConfiguration().windowConfiguration.getBounds().set(
+ activity.getTaskRecord().getConfiguration().windowConfiguration.setAppBounds(
0, 0, 1000, 2000);
final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
@@ -363,15 +363,15 @@
}
});
- // Expect the exact component name when the activity is in size compatible mode.
- activity.getResolvedOverrideConfiguration().windowConfiguration.getBounds().set(
+ // Expect the exact token when the activity is in size compatibility mode.
+ activity.getResolvedOverrideConfiguration().windowConfiguration.setAppBounds(
0, 0, 800, 1600);
resultWrapper.add(new CompletableFuture<>());
display.handleActivitySizeCompatModeIfNeeded(activity);
assertEquals(activity.appToken, resultWrapper.get(0).get(2, TimeUnit.SECONDS));
- // Expect null component name when switching to non-size-compat mode activity.
+ // Expect null token when switching to non-size-compat mode activity.
activity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
resultWrapper.set(0, new CompletableFuture<>());
display.handleActivitySizeCompatModeIfNeeded(activity);
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 457d9c0..d580557 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -43,7 +43,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
@@ -61,6 +64,8 @@
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for the {@link ActivityRecord} class.
*
@@ -175,15 +180,14 @@
@Test
public void testRestartProcessIfVisible() {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
- mActivity.getParent().getWindowConfiguration().setAppBounds(0, 0, 500, 1000);
mActivity.visible = true;
mActivity.haveState = false;
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.maxAspectRatio = 1.5f;
mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
- final Rect originalOverrideBounds = new Rect(0, 0, 400, 600);
- mActivity.setBounds(originalOverrideBounds);
+ prepareFixedAspectRatioUnresizableActivity();
+ final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
+ mTask.getWindowConfiguration().setAppBounds(0, 0, 600, 1200);
+ // The visible activity should recompute configuration according to the last parent bounds.
mService.restartActivityProcessIfVisible(mActivity.appToken);
assertEquals(ActivityStack.ActivityState.RESTARTING_PROCESS, mActivity.getState());
@@ -426,19 +430,41 @@
}
@Test
+ public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() {
+ final int decorHeight = 200; // e.g. The device has cutout.
+ final Rect parentAppBounds = new Rect(0, decorHeight, 600, 1000);
+ mTask.getWindowConfiguration().setAppBounds(parentAppBounds);
+ mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+ .when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.maxAspectRatio = 1;
+ ensureActivityConfiguration();
+
+ final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ // Ensure the app bounds keep the declared aspect ratio.
+ assertEquals(appBounds.width(), appBounds.height());
+ // The decor height should be a part of the effective bounds.
+ assertEquals(mActivity.getBounds().height(), appBounds.height() + decorHeight);
+
+ mTask.getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ // After changing orientation, the aspect ratio should be the same.
+ assertEquals(appBounds.width(), appBounds.height());
+ // The decor height will be included in width.
+ assertEquals(mActivity.getBounds().width(), appBounds.width() + decorHeight);
+ }
+
+ @Test
public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
// Initialize different bounds on a new display.
final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
- newDisplay.setBounds(0, 0, 1000, 2000);
+ newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000));
newDisplay.getConfiguration().densityDpi = 300;
- mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
mTask.getConfiguration().densityDpi = 200;
- when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.maxAspectRatio = 1.5f;
- ensureActivityConfiguration();
+ prepareFixedAspectRatioUnresizableActivity();
+
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -448,14 +474,16 @@
assertEquals(originalBounds, mActivity.getBounds());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+ assertTrue(mActivity.inSizeCompatMode());
}
@Test
public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() {
when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
- ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -465,6 +493,7 @@
ensureActivityConfiguration();
assertEquals(originalBounds, mActivity.getBounds());
+ assertTrue(mActivity.inSizeCompatMode());
}
@Test
@@ -473,10 +502,7 @@
| Configuration.SCREENLAYOUT_SIZE_NORMAL;
mTask.getConfiguration().screenLayout = fixedScreenLayout
| Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
- mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.maxAspectRatio = 1.5f;
- ensureActivityConfiguration();
+ prepareFixedAspectRatioUnresizableActivity();
// The initial configuration should inherit from parent.
assertEquals(mTask.getConfiguration().screenLayout,
@@ -490,4 +516,46 @@
assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
mActivity.getConfiguration().screenLayout);
}
+
+ @Test
+ public void testSizeCompatMode_ResetNonVisibleActivity() {
+ final ActivityDisplay display = mStack.getDisplay();
+ spyOn(display);
+
+ prepareFixedAspectRatioUnresizableActivity();
+ mActivity.setState(STOPPED, "testSizeCompatMode");
+ mActivity.visible = false;
+ mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ // Make the parent bounds to be different so the activity is in size compatibility mode.
+ mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
+
+ // Simulate the display changes orientation.
+ doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION
+ | ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
+ .when(display).getLastOverrideConfigurationChanges();
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ // The override configuration should not change so it is still in size compatibility mode.
+ assertTrue(mActivity.inSizeCompatMode());
+
+ // Simulate the display changes density.
+ doReturn(ActivityInfo.CONFIG_DENSITY).when(display).getLastOverrideConfigurationChanges();
+ mService.mAmInternal = mock(ActivityManagerInternal.class);
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ // The override configuration should be reset and the activity's process will be killed.
+ assertFalse(mActivity.inSizeCompatMode());
+ verify(mActivity).restartProcessIfVisible();
+ mService.mH.runWithScissors(() -> { }, TimeUnit.SECONDS.toMillis(3));
+ verify(mService.mAmInternal).killProcess(
+ eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
+ }
+
+ /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
+ private void prepareFixedAspectRatioUnresizableActivity() {
+ when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.maxAspectRatio = 1.5f;
+ ensureActivityConfiguration();
+ }
}
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 d17e5c3..c4009df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -218,6 +218,7 @@
final Rect fixedBounds = mToken.getRequestedOverrideConfiguration().windowConfiguration
.getBounds();
fixedBounds.set(0, 0, 1200, 1600);
+ mToken.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds(fixedBounds);
final Configuration newParentConfig = mTask.getConfiguration();
// Change the size of the container to two times smaller with insets.