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.